From e1ab8d1abe3c342c58e5f41f0a18a14c89f662a2 Mon Sep 17 00:00:00 2001 From: Flutter GitHub Bot Date: Thu, 9 Nov 2023 15:32:15 -0800 Subject: [PATCH] Automatic compilation --- .ci.yaml | 162 - .github/dependabot.yml | 89 - .github/workflows/no-response_publish.yaml | 40 - .github/workflows/no-response_test.yaml | 34 - .github/workflows/scorecards-analysis.yml | 56 - .gitignore | 13 - AUTHORS | 7 - CI_YAML.md | 310 - CODEOWNERS | 44 - CONTRIBUTING.md | 37 - LICENSE | 27 - README.md | 126 - .../no-response/action.yml => action.yml | 0 analysis_options.yaml | 52 - analyze/analyze.dart | 308 - analyze/pubspec.yaml | 15 - app_dart/.gitignore | 6 - app_dart/Dockerfile | 17 - app_dart/LICENSE | 27 - app_dart/README.md | 166 - app_dart/analysis_options.yaml | 8 - app_dart/app.yaml | 33 - app_dart/bin/generate_jspb.dart | 70 - app_dart/bin/server.dart | 320 - app_dart/bin/validate_scheduler_config.dart | 33 - app_dart/bin/validate_task_ownership.dart | 75 - app_dart/build.yaml | 8 - app_dart/cloudbuild_app_dart.yaml | 40 - app_dart/cloudbuild_app_dart_deploy.yaml | 41 - app_dart/dev/deploy.dart | 155 - app_dart/index.yaml | 48 - app_dart/integration_test/common.dart | 28 - .../integration_test/data/cocoon_config.json | 1 - .../integration_test/generate_jspb_test.dart | 28 - .../validate_all_ci_configs_test.dart | 124 - .../validate_test_ownership_test.dart | 61 - app_dart/lib/ci_yaml.dart | 6 - app_dart/lib/cocoon_service.dart | 49 - app_dart/lib/protos.dart | 5 - .../src/foundation/github_checks_util.dart | 170 - app_dart/lib/src/foundation/providers.dart | 34 - app_dart/lib/src/foundation/typedefs.dart | 18 - app_dart/lib/src/foundation/utils.dart | 245 - .../src/model/appengine/allowed_account.dart | 40 - app_dart/lib/src/model/appengine/branch.dart | 54 - .../src/model/appengine/cocoon_config.dart | 11 - app_dart/lib/src/model/appengine/commit.dart | 144 - .../lib/src/model/appengine/commit.g.dart | 14 - .../appengine/github_build_status_update.dart | 63 - .../appengine/github_gold_status_update.dart | 68 - .../src/model/appengine/key_converter.dart | 31 - .../lib/src/model/appengine/key_helper.dart | 152 - .../src/model/appengine/key_helper.pb.dart | 229 - .../model/appengine/key_helper.pbenum.dart | 6 - .../model/appengine/key_helper.pbjson.dart | 46 - .../model/appengine/key_helper.pbserver.dart | 8 - .../lib/src/model/appengine/key_helper.proto | 41 - .../lib/src/model/appengine/key_wrapper.dart | 69 - .../model/appengine/service_account_info.dart | 76 - .../appengine/service_account_info.g.dart | 35 - app_dart/lib/src/model/appengine/stage.dart | 159 - app_dart/lib/src/model/appengine/stage.g.dart | 15 - app_dart/lib/src/model/appengine/task.dart | 548 - app_dart/lib/src/model/appengine/task.g.dart | 35 - app_dart/lib/src/model/ci_yaml/ci_yaml.dart | 308 - app_dart/lib/src/model/ci_yaml/target.dart | 281 - .../lib/src/model/common/json_converters.dart | 223 - app_dart/lib/src/model/gerrit/commit.dart | 68 - app_dart/lib/src/model/gerrit/commit.g.dart | 37 - app_dart/lib/src/model/github/checks.dart | 54 - app_dart/lib/src/model/github/checks.g.dart | 44 - app_dart/lib/src/model/google/grpc.dart | 42 - app_dart/lib/src/model/google/grpc.g.dart | 31 - app_dart/lib/src/model/google/token_info.dart | 124 - .../lib/src/model/google/token_info.g.dart | 53 - app_dart/lib/src/model/luci/buildbucket.dart | 928 - .../lib/src/model/luci/buildbucket.g.dart | 532 - app_dart/lib/src/model/luci/push_message.dart | 336 - .../lib/src/model/luci/push_message.g.dart | 173 - .../internal/build_status_response.pb.dart | 80 - .../build_status_response.pbenum.dart | 27 - .../build_status_response.pbjson.dart | 35 - .../build_status_response.pbserver.dart | 8 - .../internal/build_status_response.proto | 17 - .../proto/internal/github_webhook.pb.dart | 83 - .../proto/internal/github_webhook.pbenum.dart | 6 - .../proto/internal/github_webhook.pbjson.dart | 23 - .../internal/github_webhook.pbserver.dart | 8 - .../model/proto/internal/github_webhook.proto | 16 - .../lib/src/model/proto/internal/key.pb.dart | 196 - .../src/model/proto/internal/key.pbenum.dart | 6 - .../src/model/proto/internal/key.pbjson.dart | 40 - .../model/proto/internal/key.pbserver.dart | 8 - .../lib/src/model/proto/internal/key.proto | 23 - .../model/proto/internal/scheduler.pb.dart | 437 - .../proto/internal/scheduler.pbenum.dart | 33 - .../proto/internal/scheduler.pbjson.dart | 186 - .../proto/internal/scheduler.pbserver.dart | 8 - .../src/model/proto/internal/scheduler.proto | 84 - app_dart/lib/src/model/proto/protos.dart | 8 - .../check_flaky_builders.dart | 229 - .../src/request_handlers/create_branch.dart | 38 - .../dart_internal_subscription.dart | 106 - .../file_flaky_issue_and_pr.dart | 206 - .../request_handlers/flaky_handler_utils.dart | 480 - .../lib/src/request_handlers/flush_cache.dart | 50 - .../request_handlers/get_build_status.dart | 42 - .../request_handlers/get_green_commits.dart | 78 - .../get_release_branches.dart | 48 - .../lib/src/request_handlers/get_repos.dart | 26 - .../lib/src/request_handlers/get_status.dart | 101 - .../github/webhook_subscription.dart | 638 - .../github_rate_limit_status.dart | 53 - .../src/request_handlers/github_webhook.dart | 74 - .../postsubmit_luci_subscription.dart | 137 - .../presubmit_luci_subscription.dart | 139 - .../push_build_status_to_github.dart | 94 - .../push_gold_status_to_github.dart | 376 - .../src/request_handlers/readiness_check.dart | 18 - .../src/request_handlers/reset_prod_task.dart | 191 - .../src/request_handlers/reset_try_task.dart | 61 - .../scheduler/batch_backfiller.dart | 245 - .../scheduler/request_subscription.dart | 109 - .../scheduler/vacuum_stale_tasks.dart | 84 - .../src/request_handlers/test_ownership.dart | 210 - .../update_existing_flaky_issues.dart | 176 - .../request_handlers/update_task_status.dart | 126 - .../vacuum_github_commits.dart | 121 - .../request_handling/api_request_handler.dart | 168 - .../src/request_handling/authentication.dart | 178 - app_dart/lib/src/request_handling/body.dart | 74 - .../cache_request_handler.dart | 78 - .../lib/src/request_handling/exceptions.dart | 69 - .../no_auth_request_handler.dart | 119 - app_dart/lib/src/request_handling/pubsub.dart | 43 - .../pubsub_authentication.dart | 80 - .../src/request_handling/request_handler.dart | 172 - .../request_handling/static_file_handler.dart | 61 - .../subscription_handler.dart | 160 - .../swarming_authentication.dart | 123 - .../src/service/access_client_provider.dart | 15 - .../src/service/access_token_provider.dart | 36 - app_dart/lib/src/service/bigquery.dart | 248 - app_dart/lib/src/service/branch_service.dart | 171 - .../src/service/build_status_provider.dart | 222 - app_dart/lib/src/service/buildbucket.dart | 210 - app_dart/lib/src/service/cache_service.dart | 217 - app_dart/lib/src/service/commit_service.dart | 95 - app_dart/lib/src/service/config.dart | 443 - app_dart/lib/src/service/datastore.dart | 338 - app_dart/lib/src/service/exceptions.dart | 23 - app_dart/lib/src/service/gerrit_service.dart | 180 - .../src/service/github_checks_service.dart | 238 - app_dart/lib/src/service/github_service.dart | 336 - app_dart/lib/src/service/logging.dart | 7 - .../lib/src/service/luci_build_service.dart | 728 - app_dart/lib/src/service/scheduler.dart | 608 - .../lib/src/service/scheduler/policy.dart | 119 - app_dart/pubspec.yaml | 55 - app_dart/test/foundation/utils_test.dart | 392 - app_dart/test/model/buildbucket_test.dart | 84 - app_dart/test/model/ci_yaml/ci_yaml_test.dart | 346 - app_dart/test/model/ci_yaml/target_test.dart | 234 - app_dart/test/model/commit_test.dart | 61 - app_dart/test/model/gerrit/commit_test.dart | 37 - .../test/model/github/checks_test_data.dart | 822 - .../test/model/google/token_info_test.dart | 53 - app_dart/test/model/model_test.dart | 132 - app_dart/test/model/push_message_test.dart | 66 - app_dart/test/model/stage_test.dart | 173 - app_dart/test/model/task_test.dart | 354 - .../check_flaky_builders_test.dart | 670 - .../check_flaky_builders_test_data.dart | 300 - .../request_handlers/create_branch_test.dart | 37 - .../dart_internal_subscription_test.dart | 308 - .../file_flaky_issue_and_pr_test.dart | 694 - .../file_flaky_issue_and_pr_test_data.dart | 384 - .../flaky_handler_utiles_test.dart | 158 - .../request_handlers/flush_cache_test.dart | 67 - .../get_green_commits_test.dart | 199 - .../request_handlers/get_status_test.dart | 191 - .../github/webhook_subscription_test.dart | 2322 - .../request_handlers/github_webhook_test.dart | 83 - .../postsubmit_luci_subscription_test.dart | 357 - .../presubmit_luci_subscription_test.dart | 186 - .../push_build_status_to_github_test.dart | 169 - .../push_gold_status_to_github_test.dart | 1604 - .../reset_prod_task_test.dart | 188 - .../request_handlers/reset_try_task_test.dart | 99 - .../scheduler/batch_backfiller_test.dart | 284 - .../scheduler/request_subscription_test.dart | 113 - .../scheduler/vacuum_stale_tasks_test.dart | 106 - .../update_existing_flaky_issues_test.dart | 474 - ...pdate_existing_flaky_issues_test_data.dart | 336 - .../update_task_status_test.dart | 174 - .../vacuum_github_commits_test.dart | 182 - .../api_request_handler_test.dart | 177 - .../request_handling/authentication_test.dart | 173 - .../cache_request_handler_test.dart | 108 - .../pubsub_authentication_test.dart | 145 - .../request_handler_test.dart | 212 - .../static_file_handler_test.dart | 84 - .../subscription_handler_test.dart | 171 - .../swarming_authentication_test.dart | 106 - app_dart/test/service/bigquery_test.dart | 111 - .../test/service/branch_service_test.dart | 172 - .../service/build_status_provider_test.dart | 249 - app_dart/test/service/buildbucket_test.dart | 377 - app_dart/test/service/cache_service_test.dart | 269 - .../test/service/commit_service_test.dart | 184 - app_dart/test/service/config_test.dart | 60 - app_dart/test/service/datastore_test.dart | 247 - .../test/service/gerrit_service_test.dart | 257 - .../service/github_checks_service_test.dart | 286 - .../test/service/github_service_test.dart | 110 - .../test/service/luci_build_service_test.dart | 1070 - .../test/service/scheduler/graph_test.dart | 372 - .../test/service/scheduler/policy_test.dart | 157 - app_dart/test/service/scheduler_test.dart | 1181 - .../src/bigquery/fake_tabledata_resource.dart | 47 - app_dart/test/src/datastore/fake_config.dart | 305 - .../test/src/datastore/fake_datastore.dart | 336 - .../api_request_handler_tester.dart | 41 - .../request_handling/fake_authentication.dart | 124 - .../test/src/request_handling/fake_http.dart | 668 - .../src/request_handling/fake_pubsub.dart | 22 - .../fake_request_handler.dart | 29 - .../no_auth_request_handler_tester.dart | 36 - .../request_handler_tester.dart | 55 - .../request_handling/subscription_tester.dart | 42 - .../test/src/service/fake_auth_client.dart | 74 - .../src/service/fake_bigquery_service.dart | 19 - .../service/fake_build_status_provider.dart | 51 - .../test/src/service/fake_buildbucket.dart | 150 - .../test/src/service/fake_gerrit_service.dart | 50 - .../test/src/service/fake_github_service.dart | 155 - .../test/src/service/fake_graphql_client.dart | 127 - .../src/service/fake_luci_build_service.dart | 32 - app_dart/test/src/service/fake_scheduler.dart | 294 - .../test/src/utilities/entity_generators.dart | 296 - app_dart/test/src/utilities/matchers.dart | 15 - app_dart/test/src/utilities/mocks.dart | 104 - app_dart/test/src/utilities/mocks.mocks.dart | 8811 ---- app_dart/test/src/utilities/push_message.dart | 197 - .../src/utilities/webhook_generators.dart | 1072 - app_dart/tool/build.sh | 44 - app_dart/tool/ensure_file | 3 - auto_submit/.dockerignore | 9 - auto_submit/.gitignore | 6 - auto_submit/CHANGELOG.md | 3 - auto_submit/Dockerfile | 17 - auto_submit/LICENSE | 27 - auto_submit/README.md | 3 - auto_submit/analysis_options.yaml | 9 - auto_submit/app.yaml | 18 - auto_submit/bin/server.dart | 62 - auto_submit/build.yaml | 8 - auto_submit/cloudbuild_auto_submit.yaml | 32 - .../cloudbuild_auto_submit_deploy.yaml | 41 - .../lib/action/git_cli_revert_method.dart | 93 - auto_submit/lib/action/revert_method.dart | 11 - .../repository_configuration.dart | 126 - .../repository_configuration_manager.dart | 167 - .../lib/exception/bigquery_exception.dart | 13 - .../exception/configuration_exception.dart | 13 - .../lib/exception/retryable_exception.dart | 13 - auto_submit/lib/foundation/providers.dart | 16 - auto_submit/lib/foundation/typedefs.dart | 11 - auto_submit/lib/git/cli_command.dart | 58 - auto_submit/lib/git/git_cli.dart | 335 - .../lib/git/git_repository_manager.dart | 101 - auto_submit/lib/git/utilities.dart | 22 - auto_submit/lib/helpers.dart | 77 - .../lib/model/auto_submit_query_result.dart | 241 - .../lib/model/auto_submit_query_result.g.dart | 160 - .../model/big_query_pull_request_record.dart | 41 - .../big_query_pull_request_record.g.dart | 31 - .../lib/model/pull_request_data_types.dart | 19 - .../lib/request_handling/authentication.dart | 44 - auto_submit/lib/request_handling/pubsub.dart | 74 - .../lib/requests/check_pull_request.dart | 101 - auto_submit/lib/requests/check_request.dart | 52 - .../lib/requests/check_revert_request.dart | 105 - auto_submit/lib/requests/exceptions.dart | 69 - .../requests/github_pull_request_event.dart | 34 - .../requests/github_pull_request_event.g.dart | 20 - auto_submit/lib/requests/github_webhook.dart | 104 - auto_submit/lib/requests/graphql_queries.dart | 187 - auto_submit/lib/requests/readiness_check.dart | 26 - .../server/authenticated_request_handler.dart | 37 - auto_submit/lib/server/request_handler.dart | 57 - .../lib/service/access_client_provider.dart | 15 - auto_submit/lib/service/approver_service.dart | 69 - auto_submit/lib/service/bigquery.dart | 410 - auto_submit/lib/service/config.dart | 284 - auto_submit/lib/service/github_service.dart | 271 - auto_submit/lib/service/graphql_service.dart | 53 - auto_submit/lib/service/log.dart | 7 - auto_submit/lib/service/process_method.dart | 11 - .../pull_request_validation_service.dart | 145 - .../service/revert_issue_body_formatter.dart | 53 - .../revert_request_validation_service.dart | 263 - auto_submit/lib/service/secrets.dart | 78 - .../lib/service/validation_service.dart | 223 - auto_submit/lib/validations/approval.dart | 180 - .../lib/validations/ci_successful.dart | 199 - auto_submit/lib/validations/empty_checks.dart | 34 - auto_submit/lib/validations/mergeable.dart | 48 - .../lib/validations/required_check_runs.dart | 114 - auto_submit/lib/validations/validation.dart | 91 - .../lib/validations/validation_filter.dart | 79 - auto_submit/pubspec.yaml | 41 - .../repository_configuration_data.dart | 48 - ...repository_configuration_manager_test.dart | 354 - .../repository_configuration_test.dart | 99 - auto_submit/test/git/cli_command_test.dart | 26 - auto_submit/test/git/git_cli_test.dart | 79 - .../test/git/git_repository_manager_test.dart | 64 - .../model/auto_submit_query_result_test.dart | 178 - .../test/model/pull_request_change_type.dart | 17 - .../request_handling/authentication_test.dart | 31 - .../requests/check_pull_request_test.dart | 810 - .../test/requests/github_webhook_test.dart | 63 - .../requests/github_webhook_test_data.dart | 562 - .../test/service/approver_service_test.dart | 56 - auto_submit/test/service/bigquery_test.dart | 591 - auto_submit/test/service/config_test.dart | 86 - .../test/service/config_test_data.dart | 69 - .../test/service/github_service_test.dart | 74 - .../pull_request_validation_service_test.dart | 446 - .../revert_issue_body_formatter_test.dart | 29 - ...evert_request_validation_service_test.dart | 1141 - .../test/src/action/fake_revert_method.dart | 20 - ...fake_repository_configuration_manager.dart | 38 - .../request_handling/fake_authentication.dart | 25 - .../src/request_handling/fake_pubsub.dart | 57 - .../src/service/fake_approver_service.dart | 15 - .../src/service/fake_bigquery_service.dart | 19 - auto_submit/test/src/service/fake_config.dart | 96 - .../test/src/service/fake_github_service.dart | 390 - .../test/src/service/fake_graphql_client.dart | 134 - .../test/src/validations/fake_approval.dart | 24 - .../test/src/validations/fake_mergeable.dart | 22 - .../validations/fake_required_check_runs.dart | 23 - .../validations/fake_validation_filter.dart | 19 - auto_submit/test/utilities/mocks.dart | 24 - auto_submit/test/utilities/mocks.mocks.dart | 3169 -- auto_submit/test/utilities/utils.dart | 137 - .../test/validations/approval_test.dart | 297 - .../test/validations/approval_test_data.dart | 242 - .../test/validations/ci_successful_test.dart | 515 - .../validations/ci_successful_test_data.dart | 179 - .../test/validations/mergeable_test.dart | 112 - .../test/validations/revert_test_data.dart | 767 - cipd_packages/README.md | 77 - cipd_packages/codesign/.gitignore | 6 - cipd_packages/codesign/LICENSE | 27 - cipd_packages/codesign/README.md | 56 - cipd_packages/codesign/analysis_options.yaml | 1 - cipd_packages/codesign/bin/codesign.dart | 158 - cipd_packages/codesign/bin/verify.dart | 29 - cipd_packages/codesign/lib/codesign.dart | 6 - .../lib/src/file_codesign_visitor.dart | 499 - cipd_packages/codesign/lib/src/log.dart | 7 - cipd_packages/codesign/lib/src/utils.dart | 117 - cipd_packages/codesign/lib/verify.dart | 185 - cipd_packages/codesign/pubspec.yaml | 22 - .../test/file_codesign_visitor_test.dart | 1249 - .../test/src/fake_process_manager.dart | 446 - cipd_packages/codesign/test/verify_test.dart | 162 - cipd_packages/codesign/tool/build.sh | 36 - cipd_packages/codesign/tool/ensure_file | 3 - cipd_packages/device_doctor/.gitignore | 11 - cipd_packages/device_doctor/LICENSE | 27 - cipd_packages/device_doctor/README.md | 89 - .../bin/ios_debug_symbol_doctor.dart | 32 - cipd_packages/device_doctor/bin/main.dart | 90 - .../device_doctor/lib/device_doctor.dart | 10 - .../device_doctor/lib/src/android_device.dart | 505 - .../device_doctor/lib/src/device.dart | 55 - .../device_doctor/lib/src/health.dart | 102 - .../device_doctor/lib/src/host_utils.dart | 60 - .../lib/src/ios_debug_symbol_doctor.dart | 253 - .../device_doctor/lib/src/ios_device.dart | 285 - cipd_packages/device_doctor/lib/src/mac.dart | 35 - .../device_doctor/lib/src/utils.dart | 167 - cipd_packages/device_doctor/pubspec.yaml | 22 - .../test/src/android_device_test.dart | 573 - .../test/src/fake_ios_device.dart | 33 - .../device_doctor/test/src/health_test.dart | 112 - .../test/src/host_utils_test.dart | 81 - .../src/ios_debug_symbol_doctor_test.dart | 325 - .../test/src/ios_device_test.dart | 505 - .../device_doctor/test/src/mac_test.dart | 57 - .../device_doctor/test/src/utils.dart | 128 - .../device_doctor/test/src/utils_test.dart | 121 - cipd_packages/device_doctor/tool/build.bat | 35 - cipd_packages/device_doctor/tool/build.sh | 48 - cipd_packages/device_doctor/tool/ensure_file | 3 - .../device_doctor/tool/ensure_file_windows | 3 - .../tool/infra-dialog/.gitignore | 1 - .../infra-dialog.xcodeproj/project.pbxproj | 307 - .../contents.xcworkspacedata | 7 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../xcschemes/infra-dialog.xcscheme | 52 - .../infra-dialogUITests/Info.plist | 22 - .../infra_dialogUITests.swift | 47 - cipd_packages/doxygen/.gitignore | 12 - cipd_packages/doxygen/LICENSE | 27 - cipd_packages/doxygen/README.md | 47 - cipd_packages/doxygen/tool/build.sh | 67 - cipd_packages/ruby/LICENSE | 27 - cipd_packages/ruby/README | 19 - cipd_packages/ruby/cocoapods_version.txt | 1 - cipd_packages/ruby/ruby_metadata.txt | 1 - .../ruby/third_party/ruby_ship/LICENSE.txt | 22 - .../ruby/third_party/ruby_ship/README | 2 - .../ruby_ship/auto_relink_dylibs.rb | 67 - .../third_party/ruby_ship/ruby_ship_build.sh | 261 - cipd_packages/ruby/tools/build.sh | 68 - cloud_build/dashboard_build.sh | 20 - cloud_build/deploy_app_dart.sh | 16 - cloud_build/deploy_auto_submit.sh | 15 - cloud_build/deploy_cron_jobs.sh | 9 - cloud_build/get_docker_image_provenance.sh | 38 - cloud_build/verify_provenance.sh | 44 - cloudbuild_cron.yaml | 22 - cron.yaml | 81 - dashboard/.gitignore | 102 - dashboard/.metadata | 45 - dashboard/LICENSE | 27 - dashboard/README.md | 111 - dashboard/USER_GUIDE.md | 53 - dashboard/analysis_options.yaml | 9 - dashboard/android/.gitignore | 13 - dashboard/android/app/build.gradle | 78 - .../android/app/src/debug/AndroidManifest.xml | 7 - .../android/app/src/main/AndroidManifest.xml | 42 - .../dashboard/MainActivity.kt | 13 - .../res/drawable-v21/launch_background.xml | 12 - .../main/res/drawable/launch_background.xml | 12 - .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin 544 -> 0 bytes .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin 442 -> 0 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 721 -> 0 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 1031 -> 0 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 1443 -> 0 bytes .../app/src/main/res/values-night/styles.xml | 18 - .../app/src/main/res/values/styles.xml | 18 - .../app/src/profile/AndroidManifest.xml | 7 - dashboard/android/build.gradle | 31 - dashboard/android/gradle.properties | 3 - .../gradle/wrapper/gradle-wrapper.properties | 5 - dashboard/android/settings.gradle | 20 - dashboard/assets/apple.png | Bin 2411 -> 0 bytes dashboard/assets/chromium.png | Bin 20108 -> 0 bytes dashboard/assets/fuchsia.png | Bin 29528 -> 0 bytes dashboard/assets/googleLogo.png | Bin 1307 -> 0 bytes dashboard/assets/linux.png | Bin 2880 -> 0 bytes dashboard/assets/windows.png | Bin 1463 -> 0 bytes dashboard/ios/.gitignore | 32 - dashboard/ios/Flutter/AppFrameworkInfo.plist | 26 - dashboard/ios/Flutter/Debug.xcconfig | 2 - dashboard/ios/Flutter/Release.xcconfig | 2 - dashboard/ios/Podfile | 41 - .../ios/Runner.xcodeproj/project.pbxproj | 471 - .../contents.xcworkspacedata | 7 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../xcshareddata/WorkspaceSettings.xcsettings | 8 - .../xcshareddata/xcschemes/Runner.xcscheme | 91 - .../contents.xcworkspacedata | 7 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../xcshareddata/WorkspaceSettings.xcsettings | 8 - dashboard/ios/Runner/AppDelegate.swift | 13 - .../AppIcon.appiconset/Contents.json | 122 - .../Icon-App-1024x1024@1x.png | Bin 10932 -> 0 bytes .../AppIcon.appiconset/Icon-App-20x20@1x.png | Bin 564 -> 0 bytes .../AppIcon.appiconset/Icon-App-20x20@2x.png | Bin 1283 -> 0 bytes .../AppIcon.appiconset/Icon-App-20x20@3x.png | Bin 1588 -> 0 bytes .../AppIcon.appiconset/Icon-App-29x29@1x.png | Bin 1025 -> 0 bytes .../AppIcon.appiconset/Icon-App-29x29@2x.png | Bin 1716 -> 0 bytes .../AppIcon.appiconset/Icon-App-29x29@3x.png | Bin 1920 -> 0 bytes .../AppIcon.appiconset/Icon-App-40x40@1x.png | Bin 1283 -> 0 bytes .../AppIcon.appiconset/Icon-App-40x40@2x.png | Bin 1895 -> 0 bytes .../AppIcon.appiconset/Icon-App-40x40@3x.png | Bin 2665 -> 0 bytes .../AppIcon.appiconset/Icon-App-60x60@2x.png | Bin 2665 -> 0 bytes .../AppIcon.appiconset/Icon-App-60x60@3x.png | Bin 3831 -> 0 bytes .../AppIcon.appiconset/Icon-App-76x76@1x.png | Bin 1888 -> 0 bytes .../AppIcon.appiconset/Icon-App-76x76@2x.png | Bin 3294 -> 0 bytes .../Icon-App-83.5x83.5@2x.png | Bin 3612 -> 0 bytes .../LaunchImage.imageset/Contents.json | 23 - .../LaunchImage.imageset/LaunchImage.png | Bin 68 -> 0 bytes .../LaunchImage.imageset/LaunchImage@2x.png | Bin 68 -> 0 bytes .../LaunchImage.imageset/LaunchImage@3x.png | Bin 68 -> 0 bytes .../LaunchImage.imageset/README.md | 5 - .../Runner/Base.lproj/LaunchScreen.storyboard | 37 - .../ios/Runner/Base.lproj/Main.storyboard | 26 - dashboard/ios/Runner/Info.plist | 45 - dashboard/ios/Runner/Runner-Bridging-Header.h | 1 - dashboard/lib/build_dashboard_page.dart | 467 - .../lib/dashboard_navigation_drawer.dart | 44 - dashboard/lib/logic/brooks.dart | 80 - dashboard/lib/logic/links.dart | 65 - dashboard/lib/logic/qualified_task.dart | 84 - dashboard/lib/logic/task_grid_filter.dart | 281 - dashboard/lib/main.dart | 98 - dashboard/lib/model/branch.pb.dart | 115 - dashboard/lib/model/branch.pbenum.dart | 6 - dashboard/lib/model/branch.pbjson.dart | 31 - dashboard/lib/model/branch.pbserver.dart | 8 - dashboard/lib/model/branch.proto | 14 - .../lib/model/build_status_response.pb.dart | 79 - .../model/build_status_response.pbenum.dart | 27 - .../model/build_status_response.pbjson.dart | 35 - .../model/build_status_response.pbserver.dart | 8 - .../lib/model/build_status_response.proto | 17 - dashboard/lib/model/commit.pb.dart | 188 - dashboard/lib/model/commit.pbenum.dart | 6 - dashboard/lib/model/commit.pbjson.dart | 29 - dashboard/lib/model/commit.pbserver.dart | 8 - dashboard/lib/model/commit.proto | 21 - dashboard/lib/model/commit_status.pb.dart | 94 - dashboard/lib/model/commit_status.pbenum.dart | 6 - dashboard/lib/model/commit_status.pbjson.dart | 24 - .../lib/model/commit_status.pbserver.dart | 8 - dashboard/lib/model/commit_status.proto | 16 - dashboard/lib/model/key.pb.dart | 194 - dashboard/lib/model/key.pbenum.dart | 6 - dashboard/lib/model/key.pbjson.dart | 40 - dashboard/lib/model/key.pbserver.dart | 8 - dashboard/lib/model/key.proto | 23 - dashboard/lib/model/task.pb.dart | 376 - dashboard/lib/model/task.pbenum.dart | 6 - dashboard/lib/model/task.pbjson.dart | 40 - dashboard/lib/model/task.pbserver.dart | 8 - dashboard/lib/model/task.proto | 31 - dashboard/lib/service/appengine_cocoon.dart | 299 - dashboard/lib/service/cocoon.dart | 84 - dashboard/lib/service/dev_cocoon.dart | 394 - .../lib/service/google_authentication.dart | 99 - dashboard/lib/state/build.dart | 437 - dashboard/lib/state/index.dart | 33 - dashboard/lib/widgets/app_bar.dart | 38 - .../lib/widgets/commit_author_avatar.dart | 53 - dashboard/lib/widgets/commit_box.dart | 214 - .../lib/widgets/error_brook_watcher.dart | 76 - .../lib/widgets/filter_property_sheet.dart | 330 - dashboard/lib/widgets/header_text.dart | 22 - dashboard/lib/widgets/lattice.dart | 882 - .../widgets/luci_task_attempt_summary.dart | 56 - dashboard/lib/widgets/now.dart | 81 - dashboard/lib/widgets/progress_button.dart | 82 - .../sign_in_button/sign_in_button.dart | 5 - .../sign_in_button/sign_in_button_native.dart | 25 - .../sign_in_button/sign_in_button_web.dart | 18 - dashboard/lib/widgets/state_provider.dart | 51 - dashboard/lib/widgets/task_box.dart | 76 - dashboard/lib/widgets/task_grid.dart | 386 - dashboard/lib/widgets/task_icon.dart | 133 - dashboard/lib/widgets/task_overlay.dart | 328 - dashboard/lib/widgets/user_sign_in.dart | 70 - dashboard/lib/widgets/web_image.dart | 66 - dashboard/linux/.gitignore | 1 - dashboard/linux/CMakeLists.txt | 106 - dashboard/linux/flutter/CMakeLists.txt | 91 - .../linux/flutter/generated_plugins.cmake | 24 - dashboard/linux/main.cc | 6 - dashboard/linux/my_application.cc | 104 - dashboard/linux/my_application.h | 18 - dashboard/macos/.gitignore | 6 - dashboard/macos/Podfile | 40 - dashboard/macos/Podfile.lock | 22 - .../macos/Runner.xcodeproj/project.pbxproj | 632 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - .../xcshareddata/xcschemes/Runner.xcscheme | 89 - .../contents.xcworkspacedata | 10 - .../xcshareddata/IDEWorkspaceChecks.plist | 8 - dashboard/macos/Runner/AppDelegate.swift | 9 - .../AppIcon.appiconset/Contents.json | 68 - .../AppIcon.appiconset/app_icon_1024.png | Bin 46993 -> 0 bytes .../AppIcon.appiconset/app_icon_128.png | Bin 3276 -> 0 bytes .../AppIcon.appiconset/app_icon_16.png | Bin 1429 -> 0 bytes .../AppIcon.appiconset/app_icon_256.png | Bin 5933 -> 0 bytes .../AppIcon.appiconset/app_icon_32.png | Bin 1243 -> 0 bytes .../AppIcon.appiconset/app_icon_512.png | Bin 14800 -> 0 bytes .../AppIcon.appiconset/app_icon_64.png | Bin 1874 -> 0 bytes .../macos/Runner/Base.lproj/MainMenu.xib | 339 - .../macos/Runner/Configs/AppInfo.xcconfig | 14 - dashboard/macos/Runner/Configs/Debug.xcconfig | 2 - .../macos/Runner/Configs/Release.xcconfig | 2 - .../macos/Runner/Configs/Warnings.xcconfig | 13 - .../macos/Runner/DebugProfile.entitlements | 12 - dashboard/macos/Runner/Info.plist | 32 - .../macos/Runner/MainFlutterWindow.swift | 15 - dashboard/macos/Runner/Release.entitlements | 8 - dashboard/pubspec.yaml | 87 - dashboard/regen_mocks.sh | 10 - dashboard/test/build_dashboard_page_test.dart | 601 - .../dashboard_navigation_drawer_test.dart | 133 - ...ld_dashboard.defaultPropertySheet.dark.png | Bin 41995 -> 0 bytes .../build_dashboard.defaultPropertySheet.png | Bin 44934 -> 0 bytes dashboard/test/logic/qualified_task_test.dart | 44 - .../test/logic/task_grid_filter_test.dart | 324 - .../test/service/appengine_cocoon_test.dart | 353 - .../service/google_authentication_test.dart | 141 - dashboard/test/state/build_test.dart | 497 - dashboard/test/state/index_test.dart | 38 - .../utils/appengine_cocoon_test_data.dart | 91 - dashboard/test/utils/fake_build.dart | 112 - .../test/utils/fake_flutter_app_icons.dart | 16 - dashboard/test/utils/fake_google_account.dart | 41 - dashboard/test/utils/fake_index_state.dart | 20 - dashboard/test/utils/fake_url_launcher.dart | 38 - dashboard/test/utils/golden.dart | 54 - dashboard/test/utils/mocks.dart | 23 - dashboard/test/utils/mocks.mocks.dart | 795 - dashboard/test/utils/output.dart | 37 - dashboard/test/utils/task_icons.dart | 35 - dashboard/test/utils/wrapper.dart | 43 - .../test/widgets/accessibility_test.dart | 84 - .../widgets/commit_author_avatar_test.dart | 35 - dashboard/test/widgets/commit_box_test.dart | 128 - .../widgets/goldens/commit_box_test.idle.png | Bin 28083 -> 0 bytes .../widgets/goldens/commit_box_test.open.png | Bin 31373 -> 0 bytes .../goldens/sign_in_button.authenticated.png | Bin 22873 -> 0 bytes .../sign_in_button.not_authenticated.png | Bin 23053 -> 0 bytes .../task_grid_test.dev.mouse_scroll_x.png | Bin 24573 -> 0 bytes .../task_grid_test.dev.mouse_scroll_y.png | Bin 35674 -> 0 bytes .../task_grid_test.dev.origin.dark.png | Bin 34196 -> 0 bytes .../goldens/task_grid_test.dev.origin.png | Bin 35644 -> 0 bytes .../task_grid_test.dev.scroll_x.dark.png | Bin 23910 -> 0 bytes .../goldens/task_grid_test.dev.scroll_x.png | Bin 24573 -> 0 bytes .../task_grid_test.dev.scroll_y.dark.png | Bin 34043 -> 0 bytes .../goldens/task_grid_test.dev.scroll_y.png | Bin 35674 -> 0 bytes .../goldens/task_grid_test.differentTypes.png | Bin 3555 -> 0 bytes .../widgets/goldens/task_grid_test.withL.png | Bin 2296 -> 0 bytes .../goldens/task_grid_test.withSkips.png | Bin 2435 -> 0 bytes .../goldens/task_grid_test.withoutL.png | Bin 2120 -> 0 bytes ...task_overlay_test.flaky_overlay_closed.png | Bin 25070 -> 0 bytes .../task_overlay_test.flaky_overlay_open.png | Bin 28639 -> 0 bytes .../task_overlay_test.nondevicelab_closed.png | Bin 25061 -> 0 bytes .../task_overlay_test.nondevicelab_open.png | Bin 28444 -> 0 bytes ...ask_overlay_test.normal_overlay_closed.png | Bin 26846 -> 0 bytes .../task_overlay_test.normal_overlay_open.png | Bin 30027 -> 0 bytes .../luci_task_attempt_summary_test.dart | 146 - dashboard/test/widgets/task_grid_test.dart | 882 - dashboard/test/widgets/task_icon_test.dart | 230 - dashboard/test/widgets/task_overlay_test.dart | 606 - dashboard/test/widgets/user_sign_in_test.dart | 122 - dashboard/test/widgets/web_image_test.dart | 15 - dashboard/web/favicon-failure.png | Bin 372 -> 0 bytes dashboard/web/favicon.png | Bin 363 -> 0 bytes dashboard/web/icons/Icon-192.png | Bin 5292 -> 0 bytes dashboard/web/icons/Icon-512.png | Bin 8252 -> 0 bytes dashboard/web/icons/Icon-maskable-192.png | Bin 5594 -> 0 bytes dashboard/web/icons/Icon-maskable-512.png | Bin 20998 -> 0 bytes dashboard/web/index.html | 45 - dashboard/web/manifest.json | 23 - dashboard/windows/.gitignore | 17 - dashboard/windows/CMakeLists.txt | 95 - dashboard/windows/flutter/CMakeLists.txt | 102 - .../windows/flutter/generated_plugins.cmake | 24 - dashboard/windows/runner/CMakeLists.txt | 18 - dashboard/windows/runner/Runner.rc | 121 - dashboard/windows/runner/flutter_window.cpp | 64 - dashboard/windows/runner/flutter_window.h | 39 - dashboard/windows/runner/main.cpp | 42 - dashboard/windows/runner/resource.h | 16 - .../windows/runner/resources/app_icon.ico | Bin 33772 -> 0 bytes dashboard/windows/runner/run_loop.cpp | 66 - dashboard/windows/runner/run_loop.h | 40 - dashboard/windows/runner/runner.exe.manifest | 20 - dashboard/windows/runner/utils.cpp | 64 - dashboard/windows/runner/utils.h | 19 - dashboard/windows/runner/win32_window.cpp | 245 - dashboard/windows/runner/win32_window.h | 98 - dev/githubanalysis/.gitignore | 8 - dev/githubanalysis/README.md | 34 - dev/githubanalysis/analysis_options.yaml | 211 - dev/githubanalysis/bin/githubanalysis.dart | 11 - dev/githubanalysis/lib/cache.dart | 104 - dev/githubanalysis/lib/debug_http.dart | 39 - dev/githubanalysis/lib/issue.dart | 337 - dev/githubanalysis/lib/main.dart | 869 - dev/githubanalysis/lib/team.dart | 81 - dev/githubanalysis/lib/utils.dart | 49 - dev/githubanalysis/pubspec.yaml | 11 - dev/provision_salt.sh | 110 - dev/prs_to_main.sh | 24 - dev/salt.minion.plist | 31 - dev/wiki-visualizer/.gitignore | 3 - dev/wiki-visualizer/README.md | 26 - dev/wiki-visualizer/bin/wiki_visualizer.dart | 46 - dev/wiki-visualizer/lib/dot.dart | 9 - dev/wiki-visualizer/lib/wiki_page.dart | 558 - dev/wiki-visualizer/pubspec.yaml | 10 - dev/wiki-visualizer/test/general_test.dart | 23 - dist/index.js | 37550 ++++++++++++++++ format.sh | 8 - .../third_party/no-response/.eslintignore | 3 - .../third_party/no-response/.eslintrc.json | 55 - .../third_party/no-response/.gitattributes | 1 - gh_actions/third_party/no-response/.gitignore | 3 - .../third_party/no-response/.prettierignore | 3 - .../third_party/no-response/.prettierrc.yaml | 4 - gh_actions/third_party/no-response/LICENSE.md | 20 - gh_actions/third_party/no-response/README.md | 75 - .../third_party/no-response/jest.config.js | 11 - .../third_party/no-response/package-lock.json | 12427 ----- .../third_party/no-response/package.json | 49 - .../third_party/no-response/src/config.ts | 66 - .../third_party/no-response/src/main.ts | 25 - .../no-response/src/no-response.ts | 198 - .../no-response/test/config.test.ts | 96 - .../third_party/no-response/tsconfig.json | 14 - licenses/README.md | 5 - licenses/check_licenses.dart | 277 - licenses/pubspec.yaml | 13 - packages/buildbucket-dart/.gitignore | 13 - packages/buildbucket-dart/CHANGELOG.md | 21 - packages/buildbucket-dart/LICENSE | 27 - packages/buildbucket-dart/README.md | 19 - .../buildbucket-dart/analysis_options.yaml | 30 - .../example/buildbucket_pb_example.dart | 17 - .../buildbucket-dart/lib/buildbucket_pb.dart | 80 - .../luci/buildbucket/proto/build.pb.dart | 2777 -- .../luci/buildbucket/proto/build.pbenum.dart | 67 - .../luci/buildbucket/proto/build.pbjson.dart | 837 - .../buildbucket/proto/build.pbserver.dart | 8 - .../buildbucket/proto/builder_common.pb.dart | 233 - .../proto/builder_common.pbenum.dart | 10 - .../proto/builder_common.pbjson.dart | 61 - .../proto/builder_common.pbserver.dart | 8 - .../buildbucket/proto/builder_service.pb.dart | 507 - .../proto/builder_service.pbenum.dart | 38 - .../proto/builder_service.pbgrpc.dart | 104 - .../proto/builder_service.pbjson.dart | 156 - .../buildbucket/proto/builds_service.pb.dart | 2129 - .../proto/builds_service.pbenum.dart | 10 - .../proto/builds_service.pbgrpc.dart | 264 - .../proto/builds_service.pbjson.dart | 572 - .../luci/buildbucket/proto/common.pb.dart | 890 - .../luci/buildbucket/proto/common.pbenum.dart | 75 - .../luci/buildbucket/proto/common.pbjson.dart | 297 - .../buildbucket/proto/common.pbserver.dart | 8 - .../luci/buildbucket/proto/launcher.pb.dart | 245 - .../buildbucket/proto/launcher.pbenum.dart | 10 - .../buildbucket/proto/launcher.pbjson.dart | 63 - .../buildbucket/proto/notification.pb.dart | 217 - .../proto/notification.pbenum.dart | 10 - .../proto/notification.pbjson.dart | 60 - .../buildbucket/proto/project_config.pb.dart | 1348 - .../proto/project_config.pbenum.dart | 50 - .../proto/project_config.pbjson.dart | 466 - .../proto/project_config.pbserver.dart | 8 - .../luci/buildbucket/proto/step.pb.dart | 207 - .../luci/buildbucket/proto/step.pbenum.dart | 10 - .../luci/buildbucket/proto/step.pbjson.dart | 52 - .../luci/buildbucket/proto/step.pbserver.dart | 8 - .../luci/buildbucket/proto/task.pb.dart | 273 - .../luci/buildbucket/proto/task.pbenum.dart | 10 - .../luci/buildbucket/proto/task.pbjson.dart | 65 - .../luci/buildbucket/proto/task.pbserver.dart | 8 - .../proto/structmask/structmask.pb.dart | 54 - .../proto/structmask/structmask.pbenum.dart | 10 - .../proto/structmask/structmask.pbjson.dart | 26 - .../luci/resultdb/proto/v1/common.pb.dart | 646 - .../luci/resultdb/proto/v1/common.pbenum.dart | 10 - .../luci/resultdb/proto/v1/common.pbjson.dart | 164 - .../resultdb/proto/v1/common.pbserver.dart | 8 - .../luci/resultdb/proto/v1/invocation.pb.dart | 647 - .../resultdb/proto/v1/invocation.pbenum.dart | 35 - .../resultdb/proto/v1/invocation.pbjson.dart | 183 - .../proto/v1/invocation.pbserver.dart | 8 - .../luci/resultdb/proto/v1/predicate.pb.dart | 446 - .../resultdb/proto/v1/predicate.pbenum.dart | 35 - .../resultdb/proto/v1/predicate.pbjson.dart | 144 - .../resultdb/proto/v1/predicate.pbserver.dart | 8 - .../src/generated/google/protobuf/any.pb.dart | 89 - .../generated/google/protobuf/any.pbenum.dart | 10 - .../generated/google/protobuf/any.pbjson.dart | 27 - .../google/protobuf/duration.pb.dart | 81 - .../google/protobuf/duration.pbenum.dart | 10 - .../google/protobuf/duration.pbjson.dart | 28 - .../google/protobuf/duration.pbserver.dart | 8 - .../generated/google/protobuf/empty.pb.dart | 48 - .../google/protobuf/empty.pbenum.dart | 10 - .../google/protobuf/empty.pbjson.dart | 22 - .../google/protobuf/field_mask.pb.dart | 58 - .../google/protobuf/field_mask.pbenum.dart | 10 - .../google/protobuf/field_mask.pbjson.dart | 25 - .../generated/google/protobuf/struct.pb.dart | 240 - .../google/protobuf/struct.pbenum.dart | 29 - .../google/protobuf/struct.pbjson.dart | 88 - .../google/protobuf/struct.pbserver.dart | 8 - .../google/protobuf/timestamp.pb.dart | 90 - .../google/protobuf/timestamp.pbenum.dart | 10 - .../google/protobuf/timestamp.pbjson.dart | 28 - .../google/protobuf/timestamp.pbserver.dart | 8 - .../google/protobuf/wrappers.pb.dart | 460 - .../google/protobuf/wrappers.pbenum.dart | 10 - .../google/protobuf/wrappers.pbjson.dart | 121 - .../google/protobuf/wrappers.pbserver.dart | 8 - .../src/generated/google/rpc/status.pb.dart | 81 - .../generated/google/rpc/status.pbenum.dart | 10 - .../generated/google/rpc/status.pbjson.dart | 29 - packages/buildbucket-dart/pubspec.yaml | 17 - .../test/buildbucket_pb_test.dart | 20 - packages/buildbucket-dart/tool/regenerate.sh | 45 - test_utilities/bin/analyze.sh | 16 - test_utilities/bin/config_test_runner.sh | 19 - test_utilities/bin/dart_test_runner.sh | 47 - test_utilities/bin/flutter_test_runner.sh | 27 - test_utilities/bin/global_test_runner.dart | 43 - test_utilities/bin/licenses.sh | 17 - test_utilities/bin/prepare_environment.sh | 13 - test_utilities/pubspec.yaml | 15 - tests.yaml | 48 - tooling/go.mod | 195 - tooling/go.sum | 1321 - tooling/tooling_test.go | 16 - 820 files changed, 37550 insertions(+), 127403 deletions(-) delete mode 100644 .ci.yaml delete mode 100644 .github/dependabot.yml delete mode 100644 .github/workflows/no-response_publish.yaml delete mode 100644 .github/workflows/no-response_test.yaml delete mode 100644 .github/workflows/scorecards-analysis.yml delete mode 100644 .gitignore delete mode 100644 AUTHORS delete mode 100644 CI_YAML.md delete mode 100644 CODEOWNERS delete mode 100644 CONTRIBUTING.md delete mode 100644 LICENSE delete mode 100644 README.md rename gh_actions/third_party/no-response/action.yml => action.yml (100%) delete mode 100644 analysis_options.yaml delete mode 100644 analyze/analyze.dart delete mode 100644 analyze/pubspec.yaml delete mode 100644 app_dart/.gitignore delete mode 100644 app_dart/Dockerfile delete mode 100644 app_dart/LICENSE delete mode 100644 app_dart/README.md delete mode 100644 app_dart/analysis_options.yaml delete mode 100644 app_dart/app.yaml delete mode 100644 app_dart/bin/generate_jspb.dart delete mode 100644 app_dart/bin/server.dart delete mode 100644 app_dart/bin/validate_scheduler_config.dart delete mode 100644 app_dart/bin/validate_task_ownership.dart delete mode 100644 app_dart/build.yaml delete mode 100644 app_dart/cloudbuild_app_dart.yaml delete mode 100644 app_dart/cloudbuild_app_dart_deploy.yaml delete mode 100644 app_dart/dev/deploy.dart delete mode 100644 app_dart/index.yaml delete mode 100644 app_dart/integration_test/common.dart delete mode 100644 app_dart/integration_test/data/cocoon_config.json delete mode 100644 app_dart/integration_test/generate_jspb_test.dart delete mode 100644 app_dart/integration_test/validate_all_ci_configs_test.dart delete mode 100644 app_dart/integration_test/validate_test_ownership_test.dart delete mode 100644 app_dart/lib/ci_yaml.dart delete mode 100644 app_dart/lib/cocoon_service.dart delete mode 100644 app_dart/lib/protos.dart delete mode 100644 app_dart/lib/src/foundation/github_checks_util.dart delete mode 100644 app_dart/lib/src/foundation/providers.dart delete mode 100644 app_dart/lib/src/foundation/typedefs.dart delete mode 100644 app_dart/lib/src/foundation/utils.dart delete mode 100644 app_dart/lib/src/model/appengine/allowed_account.dart delete mode 100644 app_dart/lib/src/model/appengine/branch.dart delete mode 100644 app_dart/lib/src/model/appengine/cocoon_config.dart delete mode 100644 app_dart/lib/src/model/appengine/commit.dart delete mode 100644 app_dart/lib/src/model/appengine/commit.g.dart delete mode 100644 app_dart/lib/src/model/appengine/github_build_status_update.dart delete mode 100644 app_dart/lib/src/model/appengine/github_gold_status_update.dart delete mode 100644 app_dart/lib/src/model/appengine/key_converter.dart delete mode 100644 app_dart/lib/src/model/appengine/key_helper.dart delete mode 100644 app_dart/lib/src/model/appengine/key_helper.pb.dart delete mode 100644 app_dart/lib/src/model/appengine/key_helper.pbenum.dart delete mode 100644 app_dart/lib/src/model/appengine/key_helper.pbjson.dart delete mode 100644 app_dart/lib/src/model/appengine/key_helper.pbserver.dart delete mode 100644 app_dart/lib/src/model/appengine/key_helper.proto delete mode 100644 app_dart/lib/src/model/appengine/key_wrapper.dart delete mode 100644 app_dart/lib/src/model/appengine/service_account_info.dart delete mode 100644 app_dart/lib/src/model/appengine/service_account_info.g.dart delete mode 100644 app_dart/lib/src/model/appengine/stage.dart delete mode 100644 app_dart/lib/src/model/appengine/stage.g.dart delete mode 100644 app_dart/lib/src/model/appengine/task.dart delete mode 100644 app_dart/lib/src/model/appengine/task.g.dart delete mode 100644 app_dart/lib/src/model/ci_yaml/ci_yaml.dart delete mode 100644 app_dart/lib/src/model/ci_yaml/target.dart delete mode 100644 app_dart/lib/src/model/common/json_converters.dart delete mode 100644 app_dart/lib/src/model/gerrit/commit.dart delete mode 100644 app_dart/lib/src/model/gerrit/commit.g.dart delete mode 100644 app_dart/lib/src/model/github/checks.dart delete mode 100644 app_dart/lib/src/model/github/checks.g.dart delete mode 100644 app_dart/lib/src/model/google/grpc.dart delete mode 100644 app_dart/lib/src/model/google/grpc.g.dart delete mode 100644 app_dart/lib/src/model/google/token_info.dart delete mode 100644 app_dart/lib/src/model/google/token_info.g.dart delete mode 100644 app_dart/lib/src/model/luci/buildbucket.dart delete mode 100644 app_dart/lib/src/model/luci/buildbucket.g.dart delete mode 100644 app_dart/lib/src/model/luci/push_message.dart delete mode 100644 app_dart/lib/src/model/luci/push_message.g.dart delete mode 100644 app_dart/lib/src/model/proto/internal/build_status_response.pb.dart delete mode 100644 app_dart/lib/src/model/proto/internal/build_status_response.pbenum.dart delete mode 100644 app_dart/lib/src/model/proto/internal/build_status_response.pbjson.dart delete mode 100644 app_dart/lib/src/model/proto/internal/build_status_response.pbserver.dart delete mode 100644 app_dart/lib/src/model/proto/internal/build_status_response.proto delete mode 100644 app_dart/lib/src/model/proto/internal/github_webhook.pb.dart delete mode 100644 app_dart/lib/src/model/proto/internal/github_webhook.pbenum.dart delete mode 100644 app_dart/lib/src/model/proto/internal/github_webhook.pbjson.dart delete mode 100644 app_dart/lib/src/model/proto/internal/github_webhook.pbserver.dart delete mode 100644 app_dart/lib/src/model/proto/internal/github_webhook.proto delete mode 100644 app_dart/lib/src/model/proto/internal/key.pb.dart delete mode 100644 app_dart/lib/src/model/proto/internal/key.pbenum.dart delete mode 100644 app_dart/lib/src/model/proto/internal/key.pbjson.dart delete mode 100644 app_dart/lib/src/model/proto/internal/key.pbserver.dart delete mode 100644 app_dart/lib/src/model/proto/internal/key.proto delete mode 100644 app_dart/lib/src/model/proto/internal/scheduler.pb.dart delete mode 100644 app_dart/lib/src/model/proto/internal/scheduler.pbenum.dart delete mode 100644 app_dart/lib/src/model/proto/internal/scheduler.pbjson.dart delete mode 100644 app_dart/lib/src/model/proto/internal/scheduler.pbserver.dart delete mode 100644 app_dart/lib/src/model/proto/internal/scheduler.proto delete mode 100644 app_dart/lib/src/model/proto/protos.dart delete mode 100644 app_dart/lib/src/request_handlers/check_flaky_builders.dart delete mode 100644 app_dart/lib/src/request_handlers/create_branch.dart delete mode 100644 app_dart/lib/src/request_handlers/dart_internal_subscription.dart delete mode 100644 app_dart/lib/src/request_handlers/file_flaky_issue_and_pr.dart delete mode 100644 app_dart/lib/src/request_handlers/flaky_handler_utils.dart delete mode 100644 app_dart/lib/src/request_handlers/flush_cache.dart delete mode 100644 app_dart/lib/src/request_handlers/get_build_status.dart delete mode 100644 app_dart/lib/src/request_handlers/get_green_commits.dart delete mode 100644 app_dart/lib/src/request_handlers/get_release_branches.dart delete mode 100644 app_dart/lib/src/request_handlers/get_repos.dart delete mode 100644 app_dart/lib/src/request_handlers/get_status.dart delete mode 100644 app_dart/lib/src/request_handlers/github/webhook_subscription.dart delete mode 100644 app_dart/lib/src/request_handlers/github_rate_limit_status.dart delete mode 100644 app_dart/lib/src/request_handlers/github_webhook.dart delete mode 100644 app_dart/lib/src/request_handlers/postsubmit_luci_subscription.dart delete mode 100644 app_dart/lib/src/request_handlers/presubmit_luci_subscription.dart delete mode 100644 app_dart/lib/src/request_handlers/push_build_status_to_github.dart delete mode 100644 app_dart/lib/src/request_handlers/push_gold_status_to_github.dart delete mode 100644 app_dart/lib/src/request_handlers/readiness_check.dart delete mode 100644 app_dart/lib/src/request_handlers/reset_prod_task.dart delete mode 100644 app_dart/lib/src/request_handlers/reset_try_task.dart delete mode 100644 app_dart/lib/src/request_handlers/scheduler/batch_backfiller.dart delete mode 100644 app_dart/lib/src/request_handlers/scheduler/request_subscription.dart delete mode 100644 app_dart/lib/src/request_handlers/scheduler/vacuum_stale_tasks.dart delete mode 100644 app_dart/lib/src/request_handlers/test_ownership.dart delete mode 100644 app_dart/lib/src/request_handlers/update_existing_flaky_issues.dart delete mode 100644 app_dart/lib/src/request_handlers/update_task_status.dart delete mode 100644 app_dart/lib/src/request_handlers/vacuum_github_commits.dart delete mode 100644 app_dart/lib/src/request_handling/api_request_handler.dart delete mode 100644 app_dart/lib/src/request_handling/authentication.dart delete mode 100644 app_dart/lib/src/request_handling/body.dart delete mode 100644 app_dart/lib/src/request_handling/cache_request_handler.dart delete mode 100644 app_dart/lib/src/request_handling/exceptions.dart delete mode 100644 app_dart/lib/src/request_handling/no_auth_request_handler.dart delete mode 100644 app_dart/lib/src/request_handling/pubsub.dart delete mode 100644 app_dart/lib/src/request_handling/pubsub_authentication.dart delete mode 100644 app_dart/lib/src/request_handling/request_handler.dart delete mode 100644 app_dart/lib/src/request_handling/static_file_handler.dart delete mode 100644 app_dart/lib/src/request_handling/subscription_handler.dart delete mode 100644 app_dart/lib/src/request_handling/swarming_authentication.dart delete mode 100644 app_dart/lib/src/service/access_client_provider.dart delete mode 100644 app_dart/lib/src/service/access_token_provider.dart delete mode 100644 app_dart/lib/src/service/bigquery.dart delete mode 100644 app_dart/lib/src/service/branch_service.dart delete mode 100644 app_dart/lib/src/service/build_status_provider.dart delete mode 100644 app_dart/lib/src/service/buildbucket.dart delete mode 100644 app_dart/lib/src/service/cache_service.dart delete mode 100644 app_dart/lib/src/service/commit_service.dart delete mode 100644 app_dart/lib/src/service/config.dart delete mode 100644 app_dart/lib/src/service/datastore.dart delete mode 100644 app_dart/lib/src/service/exceptions.dart delete mode 100644 app_dart/lib/src/service/gerrit_service.dart delete mode 100644 app_dart/lib/src/service/github_checks_service.dart delete mode 100644 app_dart/lib/src/service/github_service.dart delete mode 100644 app_dart/lib/src/service/logging.dart delete mode 100644 app_dart/lib/src/service/luci_build_service.dart delete mode 100644 app_dart/lib/src/service/scheduler.dart delete mode 100644 app_dart/lib/src/service/scheduler/policy.dart delete mode 100644 app_dart/pubspec.yaml delete mode 100644 app_dart/test/foundation/utils_test.dart delete mode 100644 app_dart/test/model/buildbucket_test.dart delete mode 100644 app_dart/test/model/ci_yaml/ci_yaml_test.dart delete mode 100644 app_dart/test/model/ci_yaml/target_test.dart delete mode 100644 app_dart/test/model/commit_test.dart delete mode 100644 app_dart/test/model/gerrit/commit_test.dart delete mode 100644 app_dart/test/model/github/checks_test_data.dart delete mode 100644 app_dart/test/model/google/token_info_test.dart delete mode 100644 app_dart/test/model/model_test.dart delete mode 100644 app_dart/test/model/push_message_test.dart delete mode 100644 app_dart/test/model/stage_test.dart delete mode 100644 app_dart/test/model/task_test.dart delete mode 100644 app_dart/test/request_handlers/check_flaky_builders_test.dart delete mode 100644 app_dart/test/request_handlers/check_flaky_builders_test_data.dart delete mode 100644 app_dart/test/request_handlers/create_branch_test.dart delete mode 100644 app_dart/test/request_handlers/dart_internal_subscription_test.dart delete mode 100644 app_dart/test/request_handlers/file_flaky_issue_and_pr_test.dart delete mode 100644 app_dart/test/request_handlers/file_flaky_issue_and_pr_test_data.dart delete mode 100644 app_dart/test/request_handlers/flaky_handler_utiles_test.dart delete mode 100644 app_dart/test/request_handlers/flush_cache_test.dart delete mode 100644 app_dart/test/request_handlers/get_green_commits_test.dart delete mode 100644 app_dart/test/request_handlers/get_status_test.dart delete mode 100644 app_dart/test/request_handlers/github/webhook_subscription_test.dart delete mode 100644 app_dart/test/request_handlers/github_webhook_test.dart delete mode 100644 app_dart/test/request_handlers/postsubmit_luci_subscription_test.dart delete mode 100644 app_dart/test/request_handlers/presubmit_luci_subscription_test.dart delete mode 100644 app_dart/test/request_handlers/push_build_status_to_github_test.dart delete mode 100644 app_dart/test/request_handlers/push_gold_status_to_github_test.dart delete mode 100644 app_dart/test/request_handlers/reset_prod_task_test.dart delete mode 100644 app_dart/test/request_handlers/reset_try_task_test.dart delete mode 100644 app_dart/test/request_handlers/scheduler/batch_backfiller_test.dart delete mode 100644 app_dart/test/request_handlers/scheduler/request_subscription_test.dart delete mode 100644 app_dart/test/request_handlers/scheduler/vacuum_stale_tasks_test.dart delete mode 100644 app_dart/test/request_handlers/update_existing_flaky_issues_test.dart delete mode 100644 app_dart/test/request_handlers/update_existing_flaky_issues_test_data.dart delete mode 100644 app_dart/test/request_handlers/update_task_status_test.dart delete mode 100644 app_dart/test/request_handlers/vacuum_github_commits_test.dart delete mode 100644 app_dart/test/request_handling/api_request_handler_test.dart delete mode 100644 app_dart/test/request_handling/authentication_test.dart delete mode 100644 app_dart/test/request_handling/cache_request_handler_test.dart delete mode 100644 app_dart/test/request_handling/pubsub_authentication_test.dart delete mode 100644 app_dart/test/request_handling/request_handler_test.dart delete mode 100644 app_dart/test/request_handling/static_file_handler_test.dart delete mode 100644 app_dart/test/request_handling/subscription_handler_test.dart delete mode 100644 app_dart/test/request_handling/swarming_authentication_test.dart delete mode 100644 app_dart/test/service/bigquery_test.dart delete mode 100644 app_dart/test/service/branch_service_test.dart delete mode 100644 app_dart/test/service/build_status_provider_test.dart delete mode 100644 app_dart/test/service/buildbucket_test.dart delete mode 100644 app_dart/test/service/cache_service_test.dart delete mode 100644 app_dart/test/service/commit_service_test.dart delete mode 100644 app_dart/test/service/config_test.dart delete mode 100644 app_dart/test/service/datastore_test.dart delete mode 100644 app_dart/test/service/gerrit_service_test.dart delete mode 100644 app_dart/test/service/github_checks_service_test.dart delete mode 100644 app_dart/test/service/github_service_test.dart delete mode 100644 app_dart/test/service/luci_build_service_test.dart delete mode 100644 app_dart/test/service/scheduler/graph_test.dart delete mode 100644 app_dart/test/service/scheduler/policy_test.dart delete mode 100644 app_dart/test/service/scheduler_test.dart delete mode 100644 app_dart/test/src/bigquery/fake_tabledata_resource.dart delete mode 100644 app_dart/test/src/datastore/fake_config.dart delete mode 100644 app_dart/test/src/datastore/fake_datastore.dart delete mode 100644 app_dart/test/src/request_handling/api_request_handler_tester.dart delete mode 100644 app_dart/test/src/request_handling/fake_authentication.dart delete mode 100644 app_dart/test/src/request_handling/fake_http.dart delete mode 100644 app_dart/test/src/request_handling/fake_pubsub.dart delete mode 100644 app_dart/test/src/request_handling/fake_request_handler.dart delete mode 100644 app_dart/test/src/request_handling/no_auth_request_handler_tester.dart delete mode 100644 app_dart/test/src/request_handling/request_handler_tester.dart delete mode 100644 app_dart/test/src/request_handling/subscription_tester.dart delete mode 100644 app_dart/test/src/service/fake_auth_client.dart delete mode 100644 app_dart/test/src/service/fake_bigquery_service.dart delete mode 100644 app_dart/test/src/service/fake_build_status_provider.dart delete mode 100644 app_dart/test/src/service/fake_buildbucket.dart delete mode 100644 app_dart/test/src/service/fake_gerrit_service.dart delete mode 100644 app_dart/test/src/service/fake_github_service.dart delete mode 100644 app_dart/test/src/service/fake_graphql_client.dart delete mode 100644 app_dart/test/src/service/fake_luci_build_service.dart delete mode 100644 app_dart/test/src/service/fake_scheduler.dart delete mode 100644 app_dart/test/src/utilities/entity_generators.dart delete mode 100644 app_dart/test/src/utilities/matchers.dart delete mode 100644 app_dart/test/src/utilities/mocks.dart delete mode 100644 app_dart/test/src/utilities/mocks.mocks.dart delete mode 100644 app_dart/test/src/utilities/push_message.dart delete mode 100644 app_dart/test/src/utilities/webhook_generators.dart delete mode 100755 app_dart/tool/build.sh delete mode 100644 app_dart/tool/ensure_file delete mode 100644 auto_submit/.dockerignore delete mode 100644 auto_submit/.gitignore delete mode 100644 auto_submit/CHANGELOG.md delete mode 100644 auto_submit/Dockerfile delete mode 100644 auto_submit/LICENSE delete mode 100644 auto_submit/README.md delete mode 100644 auto_submit/analysis_options.yaml delete mode 100644 auto_submit/app.yaml delete mode 100644 auto_submit/bin/server.dart delete mode 100644 auto_submit/build.yaml delete mode 100644 auto_submit/cloudbuild_auto_submit.yaml delete mode 100644 auto_submit/cloudbuild_auto_submit_deploy.yaml delete mode 100644 auto_submit/lib/action/git_cli_revert_method.dart delete mode 100644 auto_submit/lib/action/revert_method.dart delete mode 100644 auto_submit/lib/configuration/repository_configuration.dart delete mode 100644 auto_submit/lib/configuration/repository_configuration_manager.dart delete mode 100644 auto_submit/lib/exception/bigquery_exception.dart delete mode 100644 auto_submit/lib/exception/configuration_exception.dart delete mode 100644 auto_submit/lib/exception/retryable_exception.dart delete mode 100644 auto_submit/lib/foundation/providers.dart delete mode 100644 auto_submit/lib/foundation/typedefs.dart delete mode 100644 auto_submit/lib/git/cli_command.dart delete mode 100644 auto_submit/lib/git/git_cli.dart delete mode 100644 auto_submit/lib/git/git_repository_manager.dart delete mode 100644 auto_submit/lib/git/utilities.dart delete mode 100644 auto_submit/lib/helpers.dart delete mode 100644 auto_submit/lib/model/auto_submit_query_result.dart delete mode 100644 auto_submit/lib/model/auto_submit_query_result.g.dart delete mode 100644 auto_submit/lib/model/big_query_pull_request_record.dart delete mode 100644 auto_submit/lib/model/big_query_pull_request_record.g.dart delete mode 100644 auto_submit/lib/model/pull_request_data_types.dart delete mode 100644 auto_submit/lib/request_handling/authentication.dart delete mode 100644 auto_submit/lib/request_handling/pubsub.dart delete mode 100644 auto_submit/lib/requests/check_pull_request.dart delete mode 100644 auto_submit/lib/requests/check_request.dart delete mode 100644 auto_submit/lib/requests/check_revert_request.dart delete mode 100644 auto_submit/lib/requests/exceptions.dart delete mode 100644 auto_submit/lib/requests/github_pull_request_event.dart delete mode 100644 auto_submit/lib/requests/github_pull_request_event.g.dart delete mode 100644 auto_submit/lib/requests/github_webhook.dart delete mode 100644 auto_submit/lib/requests/graphql_queries.dart delete mode 100644 auto_submit/lib/requests/readiness_check.dart delete mode 100644 auto_submit/lib/server/authenticated_request_handler.dart delete mode 100644 auto_submit/lib/server/request_handler.dart delete mode 100644 auto_submit/lib/service/access_client_provider.dart delete mode 100644 auto_submit/lib/service/approver_service.dart delete mode 100644 auto_submit/lib/service/bigquery.dart delete mode 100644 auto_submit/lib/service/config.dart delete mode 100644 auto_submit/lib/service/github_service.dart delete mode 100644 auto_submit/lib/service/graphql_service.dart delete mode 100644 auto_submit/lib/service/log.dart delete mode 100644 auto_submit/lib/service/process_method.dart delete mode 100644 auto_submit/lib/service/pull_request_validation_service.dart delete mode 100644 auto_submit/lib/service/revert_issue_body_formatter.dart delete mode 100644 auto_submit/lib/service/revert_request_validation_service.dart delete mode 100644 auto_submit/lib/service/secrets.dart delete mode 100644 auto_submit/lib/service/validation_service.dart delete mode 100644 auto_submit/lib/validations/approval.dart delete mode 100644 auto_submit/lib/validations/ci_successful.dart delete mode 100644 auto_submit/lib/validations/empty_checks.dart delete mode 100644 auto_submit/lib/validations/mergeable.dart delete mode 100644 auto_submit/lib/validations/required_check_runs.dart delete mode 100644 auto_submit/lib/validations/validation.dart delete mode 100644 auto_submit/lib/validations/validation_filter.dart delete mode 100644 auto_submit/pubspec.yaml delete mode 100644 auto_submit/test/configuration/repository_configuration_data.dart delete mode 100644 auto_submit/test/configuration/repository_configuration_manager_test.dart delete mode 100644 auto_submit/test/configuration/repository_configuration_test.dart delete mode 100644 auto_submit/test/git/cli_command_test.dart delete mode 100644 auto_submit/test/git/git_cli_test.dart delete mode 100644 auto_submit/test/git/git_repository_manager_test.dart delete mode 100644 auto_submit/test/model/auto_submit_query_result_test.dart delete mode 100644 auto_submit/test/model/pull_request_change_type.dart delete mode 100644 auto_submit/test/request_handling/authentication_test.dart delete mode 100644 auto_submit/test/requests/check_pull_request_test.dart delete mode 100644 auto_submit/test/requests/github_webhook_test.dart delete mode 100644 auto_submit/test/requests/github_webhook_test_data.dart delete mode 100644 auto_submit/test/service/approver_service_test.dart delete mode 100644 auto_submit/test/service/bigquery_test.dart delete mode 100644 auto_submit/test/service/config_test.dart delete mode 100644 auto_submit/test/service/config_test_data.dart delete mode 100644 auto_submit/test/service/github_service_test.dart delete mode 100644 auto_submit/test/service/pull_request_validation_service_test.dart delete mode 100644 auto_submit/test/service/revert_issue_body_formatter_test.dart delete mode 100644 auto_submit/test/service/revert_request_validation_service_test.dart delete mode 100644 auto_submit/test/src/action/fake_revert_method.dart delete mode 100644 auto_submit/test/src/configuration/fake_repository_configuration_manager.dart delete mode 100644 auto_submit/test/src/request_handling/fake_authentication.dart delete mode 100644 auto_submit/test/src/request_handling/fake_pubsub.dart delete mode 100644 auto_submit/test/src/service/fake_approver_service.dart delete mode 100644 auto_submit/test/src/service/fake_bigquery_service.dart delete mode 100644 auto_submit/test/src/service/fake_config.dart delete mode 100644 auto_submit/test/src/service/fake_github_service.dart delete mode 100644 auto_submit/test/src/service/fake_graphql_client.dart delete mode 100644 auto_submit/test/src/validations/fake_approval.dart delete mode 100644 auto_submit/test/src/validations/fake_mergeable.dart delete mode 100644 auto_submit/test/src/validations/fake_required_check_runs.dart delete mode 100644 auto_submit/test/src/validations/fake_validation_filter.dart delete mode 100644 auto_submit/test/utilities/mocks.dart delete mode 100644 auto_submit/test/utilities/mocks.mocks.dart delete mode 100644 auto_submit/test/utilities/utils.dart delete mode 100644 auto_submit/test/validations/approval_test.dart delete mode 100644 auto_submit/test/validations/approval_test_data.dart delete mode 100644 auto_submit/test/validations/ci_successful_test.dart delete mode 100644 auto_submit/test/validations/ci_successful_test_data.dart delete mode 100644 auto_submit/test/validations/mergeable_test.dart delete mode 100644 auto_submit/test/validations/revert_test_data.dart delete mode 100644 cipd_packages/README.md delete mode 100644 cipd_packages/codesign/.gitignore delete mode 100644 cipd_packages/codesign/LICENSE delete mode 100644 cipd_packages/codesign/README.md delete mode 100644 cipd_packages/codesign/analysis_options.yaml delete mode 100644 cipd_packages/codesign/bin/codesign.dart delete mode 100644 cipd_packages/codesign/bin/verify.dart delete mode 100644 cipd_packages/codesign/lib/codesign.dart delete mode 100644 cipd_packages/codesign/lib/src/file_codesign_visitor.dart delete mode 100644 cipd_packages/codesign/lib/src/log.dart delete mode 100644 cipd_packages/codesign/lib/src/utils.dart delete mode 100644 cipd_packages/codesign/lib/verify.dart delete mode 100644 cipd_packages/codesign/pubspec.yaml delete mode 100644 cipd_packages/codesign/test/file_codesign_visitor_test.dart delete mode 100644 cipd_packages/codesign/test/src/fake_process_manager.dart delete mode 100644 cipd_packages/codesign/test/verify_test.dart delete mode 100755 cipd_packages/codesign/tool/build.sh delete mode 100644 cipd_packages/codesign/tool/ensure_file delete mode 100644 cipd_packages/device_doctor/.gitignore delete mode 100644 cipd_packages/device_doctor/LICENSE delete mode 100644 cipd_packages/device_doctor/README.md delete mode 100644 cipd_packages/device_doctor/bin/ios_debug_symbol_doctor.dart delete mode 100644 cipd_packages/device_doctor/bin/main.dart delete mode 100644 cipd_packages/device_doctor/lib/device_doctor.dart delete mode 100644 cipd_packages/device_doctor/lib/src/android_device.dart delete mode 100644 cipd_packages/device_doctor/lib/src/device.dart delete mode 100644 cipd_packages/device_doctor/lib/src/health.dart delete mode 100644 cipd_packages/device_doctor/lib/src/host_utils.dart delete mode 100644 cipd_packages/device_doctor/lib/src/ios_debug_symbol_doctor.dart delete mode 100644 cipd_packages/device_doctor/lib/src/ios_device.dart delete mode 100644 cipd_packages/device_doctor/lib/src/mac.dart delete mode 100644 cipd_packages/device_doctor/lib/src/utils.dart delete mode 100644 cipd_packages/device_doctor/pubspec.yaml delete mode 100644 cipd_packages/device_doctor/test/src/android_device_test.dart delete mode 100644 cipd_packages/device_doctor/test/src/fake_ios_device.dart delete mode 100644 cipd_packages/device_doctor/test/src/health_test.dart delete mode 100644 cipd_packages/device_doctor/test/src/host_utils_test.dart delete mode 100644 cipd_packages/device_doctor/test/src/ios_debug_symbol_doctor_test.dart delete mode 100644 cipd_packages/device_doctor/test/src/ios_device_test.dart delete mode 100644 cipd_packages/device_doctor/test/src/mac_test.dart delete mode 100644 cipd_packages/device_doctor/test/src/utils.dart delete mode 100644 cipd_packages/device_doctor/test/src/utils_test.dart delete mode 100644 cipd_packages/device_doctor/tool/build.bat delete mode 100755 cipd_packages/device_doctor/tool/build.sh delete mode 100644 cipd_packages/device_doctor/tool/ensure_file delete mode 100644 cipd_packages/device_doctor/tool/ensure_file_windows delete mode 100644 cipd_packages/device_doctor/tool/infra-dialog/.gitignore delete mode 100644 cipd_packages/device_doctor/tool/infra-dialog/infra-dialog.xcodeproj/project.pbxproj delete mode 100644 cipd_packages/device_doctor/tool/infra-dialog/infra-dialog.xcodeproj/project.xcworkspace/contents.xcworkspacedata delete mode 100644 cipd_packages/device_doctor/tool/infra-dialog/infra-dialog.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 cipd_packages/device_doctor/tool/infra-dialog/infra-dialog.xcodeproj/xcshareddata/xcschemes/infra-dialog.xcscheme delete mode 100644 cipd_packages/device_doctor/tool/infra-dialog/infra-dialogUITests/Info.plist delete mode 100644 cipd_packages/device_doctor/tool/infra-dialog/infra-dialogUITests/infra_dialogUITests.swift delete mode 100644 cipd_packages/doxygen/.gitignore delete mode 100644 cipd_packages/doxygen/LICENSE delete mode 100644 cipd_packages/doxygen/README.md delete mode 100755 cipd_packages/doxygen/tool/build.sh delete mode 100644 cipd_packages/ruby/LICENSE delete mode 100644 cipd_packages/ruby/README delete mode 100644 cipd_packages/ruby/cocoapods_version.txt delete mode 100644 cipd_packages/ruby/ruby_metadata.txt delete mode 100644 cipd_packages/ruby/third_party/ruby_ship/LICENSE.txt delete mode 100644 cipd_packages/ruby/third_party/ruby_ship/README delete mode 100644 cipd_packages/ruby/third_party/ruby_ship/auto_relink_dylibs.rb delete mode 100644 cipd_packages/ruby/third_party/ruby_ship/ruby_ship_build.sh delete mode 100644 cipd_packages/ruby/tools/build.sh delete mode 100755 cloud_build/dashboard_build.sh delete mode 100755 cloud_build/deploy_app_dart.sh delete mode 100755 cloud_build/deploy_auto_submit.sh delete mode 100755 cloud_build/deploy_cron_jobs.sh delete mode 100755 cloud_build/get_docker_image_provenance.sh delete mode 100755 cloud_build/verify_provenance.sh delete mode 100644 cloudbuild_cron.yaml delete mode 100644 cron.yaml delete mode 100644 dashboard/.gitignore delete mode 100644 dashboard/.metadata delete mode 100644 dashboard/LICENSE delete mode 100644 dashboard/README.md delete mode 100644 dashboard/USER_GUIDE.md delete mode 100644 dashboard/analysis_options.yaml delete mode 100644 dashboard/android/.gitignore delete mode 100644 dashboard/android/app/build.gradle delete mode 100644 dashboard/android/app/src/debug/AndroidManifest.xml delete mode 100644 dashboard/android/app/src/main/AndroidManifest.xml delete mode 100644 dashboard/android/app/src/main/kotlin/com/appspot/flutter_dashboard/dashboard/MainActivity.kt delete mode 100644 dashboard/android/app/src/main/res/drawable-v21/launch_background.xml delete mode 100644 dashboard/android/app/src/main/res/drawable/launch_background.xml delete mode 100644 dashboard/android/app/src/main/res/mipmap-hdpi/ic_launcher.png delete mode 100644 dashboard/android/app/src/main/res/mipmap-mdpi/ic_launcher.png delete mode 100644 dashboard/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png delete mode 100644 dashboard/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png delete mode 100644 dashboard/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png delete mode 100644 dashboard/android/app/src/main/res/values-night/styles.xml delete mode 100644 dashboard/android/app/src/main/res/values/styles.xml delete mode 100644 dashboard/android/app/src/profile/AndroidManifest.xml delete mode 100644 dashboard/android/build.gradle delete mode 100644 dashboard/android/gradle.properties delete mode 100644 dashboard/android/gradle/wrapper/gradle-wrapper.properties delete mode 100644 dashboard/android/settings.gradle delete mode 100644 dashboard/assets/apple.png delete mode 100644 dashboard/assets/chromium.png delete mode 100644 dashboard/assets/fuchsia.png delete mode 100644 dashboard/assets/googleLogo.png delete mode 100644 dashboard/assets/linux.png delete mode 100644 dashboard/assets/windows.png delete mode 100644 dashboard/ios/.gitignore delete mode 100644 dashboard/ios/Flutter/AppFrameworkInfo.plist delete mode 100644 dashboard/ios/Flutter/Debug.xcconfig delete mode 100644 dashboard/ios/Flutter/Release.xcconfig delete mode 100644 dashboard/ios/Podfile delete mode 100644 dashboard/ios/Runner.xcodeproj/project.pbxproj delete mode 100644 dashboard/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata delete mode 100644 dashboard/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 dashboard/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings delete mode 100644 dashboard/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme delete mode 100644 dashboard/ios/Runner.xcworkspace/contents.xcworkspacedata delete mode 100644 dashboard/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 dashboard/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings delete mode 100644 dashboard/ios/Runner/AppDelegate.swift delete mode 100644 dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png delete mode 100644 dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png delete mode 100644 dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png delete mode 100644 dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png delete mode 100644 dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png delete mode 100644 dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png delete mode 100644 dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png delete mode 100644 dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png delete mode 100644 dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png delete mode 100644 dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png delete mode 100644 dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png delete mode 100644 dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png delete mode 100644 dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png delete mode 100644 dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png delete mode 100644 dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png delete mode 100644 dashboard/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json delete mode 100644 dashboard/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png delete mode 100644 dashboard/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png delete mode 100644 dashboard/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png delete mode 100644 dashboard/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md delete mode 100644 dashboard/ios/Runner/Base.lproj/LaunchScreen.storyboard delete mode 100644 dashboard/ios/Runner/Base.lproj/Main.storyboard delete mode 100644 dashboard/ios/Runner/Info.plist delete mode 100644 dashboard/ios/Runner/Runner-Bridging-Header.h delete mode 100644 dashboard/lib/build_dashboard_page.dart delete mode 100644 dashboard/lib/dashboard_navigation_drawer.dart delete mode 100644 dashboard/lib/logic/brooks.dart delete mode 100644 dashboard/lib/logic/links.dart delete mode 100644 dashboard/lib/logic/qualified_task.dart delete mode 100644 dashboard/lib/logic/task_grid_filter.dart delete mode 100644 dashboard/lib/main.dart delete mode 100644 dashboard/lib/model/branch.pb.dart delete mode 100644 dashboard/lib/model/branch.pbenum.dart delete mode 100644 dashboard/lib/model/branch.pbjson.dart delete mode 100644 dashboard/lib/model/branch.pbserver.dart delete mode 100644 dashboard/lib/model/branch.proto delete mode 100644 dashboard/lib/model/build_status_response.pb.dart delete mode 100644 dashboard/lib/model/build_status_response.pbenum.dart delete mode 100644 dashboard/lib/model/build_status_response.pbjson.dart delete mode 100644 dashboard/lib/model/build_status_response.pbserver.dart delete mode 100644 dashboard/lib/model/build_status_response.proto delete mode 100644 dashboard/lib/model/commit.pb.dart delete mode 100644 dashboard/lib/model/commit.pbenum.dart delete mode 100644 dashboard/lib/model/commit.pbjson.dart delete mode 100644 dashboard/lib/model/commit.pbserver.dart delete mode 100644 dashboard/lib/model/commit.proto delete mode 100644 dashboard/lib/model/commit_status.pb.dart delete mode 100644 dashboard/lib/model/commit_status.pbenum.dart delete mode 100644 dashboard/lib/model/commit_status.pbjson.dart delete mode 100644 dashboard/lib/model/commit_status.pbserver.dart delete mode 100644 dashboard/lib/model/commit_status.proto delete mode 100644 dashboard/lib/model/key.pb.dart delete mode 100644 dashboard/lib/model/key.pbenum.dart delete mode 100644 dashboard/lib/model/key.pbjson.dart delete mode 100644 dashboard/lib/model/key.pbserver.dart delete mode 100644 dashboard/lib/model/key.proto delete mode 100644 dashboard/lib/model/task.pb.dart delete mode 100644 dashboard/lib/model/task.pbenum.dart delete mode 100644 dashboard/lib/model/task.pbjson.dart delete mode 100644 dashboard/lib/model/task.pbserver.dart delete mode 100644 dashboard/lib/model/task.proto delete mode 100644 dashboard/lib/service/appengine_cocoon.dart delete mode 100644 dashboard/lib/service/cocoon.dart delete mode 100644 dashboard/lib/service/dev_cocoon.dart delete mode 100644 dashboard/lib/service/google_authentication.dart delete mode 100644 dashboard/lib/state/build.dart delete mode 100644 dashboard/lib/state/index.dart delete mode 100644 dashboard/lib/widgets/app_bar.dart delete mode 100644 dashboard/lib/widgets/commit_author_avatar.dart delete mode 100644 dashboard/lib/widgets/commit_box.dart delete mode 100644 dashboard/lib/widgets/error_brook_watcher.dart delete mode 100644 dashboard/lib/widgets/filter_property_sheet.dart delete mode 100644 dashboard/lib/widgets/header_text.dart delete mode 100644 dashboard/lib/widgets/lattice.dart delete mode 100644 dashboard/lib/widgets/luci_task_attempt_summary.dart delete mode 100644 dashboard/lib/widgets/now.dart delete mode 100644 dashboard/lib/widgets/progress_button.dart delete mode 100644 dashboard/lib/widgets/sign_in_button/sign_in_button.dart delete mode 100644 dashboard/lib/widgets/sign_in_button/sign_in_button_native.dart delete mode 100644 dashboard/lib/widgets/sign_in_button/sign_in_button_web.dart delete mode 100644 dashboard/lib/widgets/state_provider.dart delete mode 100644 dashboard/lib/widgets/task_box.dart delete mode 100644 dashboard/lib/widgets/task_grid.dart delete mode 100644 dashboard/lib/widgets/task_icon.dart delete mode 100644 dashboard/lib/widgets/task_overlay.dart delete mode 100644 dashboard/lib/widgets/user_sign_in.dart delete mode 100644 dashboard/lib/widgets/web_image.dart delete mode 100644 dashboard/linux/.gitignore delete mode 100644 dashboard/linux/CMakeLists.txt delete mode 100644 dashboard/linux/flutter/CMakeLists.txt delete mode 100644 dashboard/linux/flutter/generated_plugins.cmake delete mode 100644 dashboard/linux/main.cc delete mode 100644 dashboard/linux/my_application.cc delete mode 100644 dashboard/linux/my_application.h delete mode 100644 dashboard/macos/.gitignore delete mode 100644 dashboard/macos/Podfile delete mode 100644 dashboard/macos/Podfile.lock delete mode 100644 dashboard/macos/Runner.xcodeproj/project.pbxproj delete mode 100644 dashboard/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 dashboard/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme delete mode 100644 dashboard/macos/Runner.xcworkspace/contents.xcworkspacedata delete mode 100644 dashboard/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist delete mode 100644 dashboard/macos/Runner/AppDelegate.swift delete mode 100644 dashboard/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json delete mode 100644 dashboard/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png delete mode 100644 dashboard/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png delete mode 100644 dashboard/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png delete mode 100644 dashboard/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png delete mode 100644 dashboard/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png delete mode 100644 dashboard/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png delete mode 100644 dashboard/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png delete mode 100644 dashboard/macos/Runner/Base.lproj/MainMenu.xib delete mode 100644 dashboard/macos/Runner/Configs/AppInfo.xcconfig delete mode 100644 dashboard/macos/Runner/Configs/Debug.xcconfig delete mode 100644 dashboard/macos/Runner/Configs/Release.xcconfig delete mode 100644 dashboard/macos/Runner/Configs/Warnings.xcconfig delete mode 100644 dashboard/macos/Runner/DebugProfile.entitlements delete mode 100644 dashboard/macos/Runner/Info.plist delete mode 100644 dashboard/macos/Runner/MainFlutterWindow.swift delete mode 100644 dashboard/macos/Runner/Release.entitlements delete mode 100644 dashboard/pubspec.yaml delete mode 100755 dashboard/regen_mocks.sh delete mode 100644 dashboard/test/build_dashboard_page_test.dart delete mode 100644 dashboard/test/dashboard_navigation_drawer_test.dart delete mode 100644 dashboard/test/goldens/build_dashboard.defaultPropertySheet.dark.png delete mode 100644 dashboard/test/goldens/build_dashboard.defaultPropertySheet.png delete mode 100644 dashboard/test/logic/qualified_task_test.dart delete mode 100644 dashboard/test/logic/task_grid_filter_test.dart delete mode 100644 dashboard/test/service/appengine_cocoon_test.dart delete mode 100644 dashboard/test/service/google_authentication_test.dart delete mode 100644 dashboard/test/state/build_test.dart delete mode 100644 dashboard/test/state/index_test.dart delete mode 100644 dashboard/test/utils/appengine_cocoon_test_data.dart delete mode 100644 dashboard/test/utils/fake_build.dart delete mode 100644 dashboard/test/utils/fake_flutter_app_icons.dart delete mode 100644 dashboard/test/utils/fake_google_account.dart delete mode 100644 dashboard/test/utils/fake_index_state.dart delete mode 100644 dashboard/test/utils/fake_url_launcher.dart delete mode 100644 dashboard/test/utils/golden.dart delete mode 100644 dashboard/test/utils/mocks.dart delete mode 100644 dashboard/test/utils/mocks.mocks.dart delete mode 100644 dashboard/test/utils/output.dart delete mode 100644 dashboard/test/utils/task_icons.dart delete mode 100644 dashboard/test/utils/wrapper.dart delete mode 100644 dashboard/test/widgets/accessibility_test.dart delete mode 100644 dashboard/test/widgets/commit_author_avatar_test.dart delete mode 100644 dashboard/test/widgets/commit_box_test.dart delete mode 100644 dashboard/test/widgets/goldens/commit_box_test.idle.png delete mode 100644 dashboard/test/widgets/goldens/commit_box_test.open.png delete mode 100644 dashboard/test/widgets/goldens/sign_in_button.authenticated.png delete mode 100644 dashboard/test/widgets/goldens/sign_in_button.not_authenticated.png delete mode 100644 dashboard/test/widgets/goldens/task_grid_test.dev.mouse_scroll_x.png delete mode 100644 dashboard/test/widgets/goldens/task_grid_test.dev.mouse_scroll_y.png delete mode 100644 dashboard/test/widgets/goldens/task_grid_test.dev.origin.dark.png delete mode 100644 dashboard/test/widgets/goldens/task_grid_test.dev.origin.png delete mode 100644 dashboard/test/widgets/goldens/task_grid_test.dev.scroll_x.dark.png delete mode 100644 dashboard/test/widgets/goldens/task_grid_test.dev.scroll_x.png delete mode 100644 dashboard/test/widgets/goldens/task_grid_test.dev.scroll_y.dark.png delete mode 100644 dashboard/test/widgets/goldens/task_grid_test.dev.scroll_y.png delete mode 100644 dashboard/test/widgets/goldens/task_grid_test.differentTypes.png delete mode 100644 dashboard/test/widgets/goldens/task_grid_test.withL.png delete mode 100644 dashboard/test/widgets/goldens/task_grid_test.withSkips.png delete mode 100644 dashboard/test/widgets/goldens/task_grid_test.withoutL.png delete mode 100644 dashboard/test/widgets/goldens/task_overlay_test.flaky_overlay_closed.png delete mode 100644 dashboard/test/widgets/goldens/task_overlay_test.flaky_overlay_open.png delete mode 100644 dashboard/test/widgets/goldens/task_overlay_test.nondevicelab_closed.png delete mode 100644 dashboard/test/widgets/goldens/task_overlay_test.nondevicelab_open.png delete mode 100644 dashboard/test/widgets/goldens/task_overlay_test.normal_overlay_closed.png delete mode 100644 dashboard/test/widgets/goldens/task_overlay_test.normal_overlay_open.png delete mode 100644 dashboard/test/widgets/luci_task_attempt_summary_test.dart delete mode 100644 dashboard/test/widgets/task_grid_test.dart delete mode 100644 dashboard/test/widgets/task_icon_test.dart delete mode 100644 dashboard/test/widgets/task_overlay_test.dart delete mode 100644 dashboard/test/widgets/user_sign_in_test.dart delete mode 100644 dashboard/test/widgets/web_image_test.dart delete mode 100644 dashboard/web/favicon-failure.png delete mode 100644 dashboard/web/favicon.png delete mode 100644 dashboard/web/icons/Icon-192.png delete mode 100644 dashboard/web/icons/Icon-512.png delete mode 100644 dashboard/web/icons/Icon-maskable-192.png delete mode 100644 dashboard/web/icons/Icon-maskable-512.png delete mode 100644 dashboard/web/index.html delete mode 100644 dashboard/web/manifest.json delete mode 100644 dashboard/windows/.gitignore delete mode 100644 dashboard/windows/CMakeLists.txt delete mode 100644 dashboard/windows/flutter/CMakeLists.txt delete mode 100644 dashboard/windows/flutter/generated_plugins.cmake delete mode 100644 dashboard/windows/runner/CMakeLists.txt delete mode 100644 dashboard/windows/runner/Runner.rc delete mode 100644 dashboard/windows/runner/flutter_window.cpp delete mode 100644 dashboard/windows/runner/flutter_window.h delete mode 100644 dashboard/windows/runner/main.cpp delete mode 100644 dashboard/windows/runner/resource.h delete mode 100644 dashboard/windows/runner/resources/app_icon.ico delete mode 100644 dashboard/windows/runner/run_loop.cpp delete mode 100644 dashboard/windows/runner/run_loop.h delete mode 100644 dashboard/windows/runner/runner.exe.manifest delete mode 100644 dashboard/windows/runner/utils.cpp delete mode 100644 dashboard/windows/runner/utils.h delete mode 100644 dashboard/windows/runner/win32_window.cpp delete mode 100644 dashboard/windows/runner/win32_window.h delete mode 100644 dev/githubanalysis/.gitignore delete mode 100644 dev/githubanalysis/README.md delete mode 100644 dev/githubanalysis/analysis_options.yaml delete mode 100644 dev/githubanalysis/bin/githubanalysis.dart delete mode 100644 dev/githubanalysis/lib/cache.dart delete mode 100644 dev/githubanalysis/lib/debug_http.dart delete mode 100644 dev/githubanalysis/lib/issue.dart delete mode 100644 dev/githubanalysis/lib/main.dart delete mode 100644 dev/githubanalysis/lib/team.dart delete mode 100644 dev/githubanalysis/lib/utils.dart delete mode 100644 dev/githubanalysis/pubspec.yaml delete mode 100755 dev/provision_salt.sh delete mode 100755 dev/prs_to_main.sh delete mode 100644 dev/salt.minion.plist delete mode 100644 dev/wiki-visualizer/.gitignore delete mode 100644 dev/wiki-visualizer/README.md delete mode 100644 dev/wiki-visualizer/bin/wiki_visualizer.dart delete mode 100644 dev/wiki-visualizer/lib/dot.dart delete mode 100644 dev/wiki-visualizer/lib/wiki_page.dart delete mode 100644 dev/wiki-visualizer/pubspec.yaml delete mode 100644 dev/wiki-visualizer/test/general_test.dart create mode 100644 dist/index.js delete mode 100755 format.sh delete mode 100644 gh_actions/third_party/no-response/.eslintignore delete mode 100644 gh_actions/third_party/no-response/.eslintrc.json delete mode 100644 gh_actions/third_party/no-response/.gitattributes delete mode 100644 gh_actions/third_party/no-response/.gitignore delete mode 100644 gh_actions/third_party/no-response/.prettierignore delete mode 100644 gh_actions/third_party/no-response/.prettierrc.yaml delete mode 100644 gh_actions/third_party/no-response/LICENSE.md delete mode 100644 gh_actions/third_party/no-response/README.md delete mode 100644 gh_actions/third_party/no-response/jest.config.js delete mode 100644 gh_actions/third_party/no-response/package-lock.json delete mode 100644 gh_actions/third_party/no-response/package.json delete mode 100644 gh_actions/third_party/no-response/src/config.ts delete mode 100644 gh_actions/third_party/no-response/src/main.ts delete mode 100644 gh_actions/third_party/no-response/src/no-response.ts delete mode 100644 gh_actions/third_party/no-response/test/config.test.ts delete mode 100644 gh_actions/third_party/no-response/tsconfig.json delete mode 100644 licenses/README.md delete mode 100644 licenses/check_licenses.dart delete mode 100644 licenses/pubspec.yaml delete mode 100644 packages/buildbucket-dart/.gitignore delete mode 100644 packages/buildbucket-dart/CHANGELOG.md delete mode 100644 packages/buildbucket-dart/LICENSE delete mode 100644 packages/buildbucket-dart/README.md delete mode 100644 packages/buildbucket-dart/analysis_options.yaml delete mode 100644 packages/buildbucket-dart/example/buildbucket_pb_example.dart delete mode 100644 packages/buildbucket-dart/lib/buildbucket_pb.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/build.pb.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/build.pbenum.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/build.pbjson.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/build.pbserver.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_common.pb.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_common.pbenum.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_common.pbjson.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_common.pbserver.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_service.pb.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_service.pbenum.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_service.pbgrpc.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_service.pbjson.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builds_service.pb.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builds_service.pbenum.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builds_service.pbgrpc.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builds_service.pbjson.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/common.pb.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/common.pbenum.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/common.pbjson.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/common.pbserver.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/launcher.pb.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/launcher.pbenum.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/launcher.pbjson.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/notification.pb.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/notification.pbenum.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/notification.pbjson.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/project_config.pb.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/project_config.pbenum.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/project_config.pbjson.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/project_config.pbserver.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/step.pb.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/step.pbenum.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/step.pbjson.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/step.pbserver.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/task.pb.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/task.pbenum.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/task.pbjson.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/task.pbserver.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/common/proto/structmask/structmask.pb.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/common/proto/structmask/structmask.pbenum.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/common/proto/structmask/structmask.pbjson.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/common.pb.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/common.pbenum.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/common.pbjson.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/common.pbserver.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/invocation.pb.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/invocation.pbenum.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/invocation.pbjson.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/invocation.pbserver.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/predicate.pb.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/predicate.pbenum.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/predicate.pbjson.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/predicate.pbserver.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/google/protobuf/any.pb.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/google/protobuf/any.pbenum.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/google/protobuf/any.pbjson.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/google/protobuf/duration.pb.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/google/protobuf/duration.pbenum.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/google/protobuf/duration.pbjson.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/google/protobuf/duration.pbserver.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/google/protobuf/empty.pb.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/google/protobuf/empty.pbenum.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/google/protobuf/empty.pbjson.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/google/protobuf/field_mask.pb.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/google/protobuf/field_mask.pbenum.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/google/protobuf/field_mask.pbjson.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/google/protobuf/struct.pb.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/google/protobuf/struct.pbenum.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/google/protobuf/struct.pbjson.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/google/protobuf/struct.pbserver.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/google/protobuf/timestamp.pb.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/google/protobuf/timestamp.pbenum.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/google/protobuf/timestamp.pbjson.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/google/protobuf/timestamp.pbserver.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/google/protobuf/wrappers.pb.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/google/protobuf/wrappers.pbenum.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/google/protobuf/wrappers.pbjson.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/google/protobuf/wrappers.pbserver.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/google/rpc/status.pb.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/google/rpc/status.pbenum.dart delete mode 100644 packages/buildbucket-dart/lib/src/generated/google/rpc/status.pbjson.dart delete mode 100644 packages/buildbucket-dart/pubspec.yaml delete mode 100644 packages/buildbucket-dart/test/buildbucket_pb_test.dart delete mode 100755 packages/buildbucket-dart/tool/regenerate.sh delete mode 100755 test_utilities/bin/analyze.sh delete mode 100755 test_utilities/bin/config_test_runner.sh delete mode 100755 test_utilities/bin/dart_test_runner.sh delete mode 100755 test_utilities/bin/flutter_test_runner.sh delete mode 100755 test_utilities/bin/global_test_runner.dart delete mode 100755 test_utilities/bin/licenses.sh delete mode 100755 test_utilities/bin/prepare_environment.sh delete mode 100644 test_utilities/pubspec.yaml delete mode 100644 tests.yaml delete mode 100644 tooling/go.mod delete mode 100644 tooling/go.sum delete mode 100644 tooling/tooling_test.go diff --git a/.ci.yaml b/.ci.yaml deleted file mode 100644 index ca2ee9fa2..000000000 --- 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 9642af835..000000000 --- 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 fc3d0b450..000000000 --- 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 f82627edc..000000000 --- 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 1cc1c8fd7..000000000 --- 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 2466d2f38..000000000 --- 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 5a547b746..000000000 --- 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 - -Google Inc. -Chromium authors. diff --git a/CI_YAML.md b/CI_YAML.md deleted file mode 100644 index a8efb1eef..000000000 --- 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 ` ` -# 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 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 b38f42cac..000000000 --- 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 3bf36c821..000000000 --- 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 d5384ca42..000000000 --- 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 07eb0f8fd..000000000 --- a/README.md +++ /dev/null @@ -1,126 +0,0 @@ - -

- - - Flutter - -

-
- - -[![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: - - -#### 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 d7f6f0f37..000000000 --- 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 5cf5f25db..000000000 --- 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 main(List arguments) async { - print('STARTING ANALYSIS'); - print('cocoonRoot: ${cocoonRoot.path}'); - await run(arguments); - print('Analysis successful.'); -} - -Future run(List arguments) async { - bool assertsEnabled = false; - assert(() { - assertsEnabled = true; - return true; - }()); - if (!assertsEnabled) { - exitWithError(['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 verifyNoTrailingSpaces( - String workingDirectory, { - int minimumMatches = 100, -}) async { - final List 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 problems = []; - for (final File file in files) { - final List 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 verifyProtos(Directory workingDirectory) async { - final List errors = []; - final List 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([ - 'The following files are missing package declarations:', - ...errors, - ]); - } -} - -// UTILITY FUNCTIONS - -Future> _gitFiles(String workingDirectory, {bool runSilently = true}) async { - final EvalResult evalResult = await _evalCommand( - 'git', - ['ls-files', '-z'], - workingDirectory: workingDirectory, - runSilently: runSilently, - ); - if (evalResult.exitCode != 0) { - exitWithError([ - 'git ls-files failed with exit code ${evalResult.exitCode}', - 'stdout:', - evalResult.stdout, - 'stderr:', - evalResult.stderr, - ]); - } - final List filenames = evalResult.stdout.split('\x00'); - assert(filenames.last.isEmpty); // git ls-files gives a trailing blank 0x00 - filenames.removeLast(); - return filenames.map((String filename) => fs.file(path.join(workingDirectory, filename))).toList(); -} - -Stream _allFiles(String workingDirectory, String? extension, {required int minimumMatches}) async* { - final Set gitFileNamesSet = {}; - 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 pending = {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 _evalCommand( - String executable, - List arguments, { - required String workingDirectory, - Map? 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>> savedStdout = process.stdout.toList(); - final Future>> savedStderr = process.stderr.toList(); - final int exitCode = await process.exitCode; - final EvalResult result = EvalResult( - stdout: utf8.decode((await savedStdout).expand((List ints) => ints).toList()), - stderr: utf8.decode((await savedStderr).expand((List 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([ - 'ERROR: Last command exited with $exitCode.', - 'Command: $commandDescription', - 'Relative working directory: $relativeWorkingDir', - ]); - } - - return result; -} - -// These files legitimately require executable permissions -const Set kExecutableAllowlist = { - '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 _checkForNewExecutables() async { - // 0b001001001 - const int executableBitMask = 0x49; - final List files = await _gitFiles(cocoonRoot.path); - final List relativePaths = files.map((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 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 46bce17e5..000000000 --- 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 3c8a15727..000000000 --- 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 27652a3d4..000000000 --- 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 d5384ca42..000000000 --- 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 d135d79b2..000000000 --- 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 /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 f4cf71f90..000000000 --- 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 2a3ec5bed..000000000 --- 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 f48082b6b..000000000 --- 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 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 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 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 main(List 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 e278599e5..000000000 --- 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 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> handlers = >{ - '/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( - cache: cache, - config: config, - delegate: GetBuildStatus(config: config), - ttl: const Duration(seconds: 15), - ), - '/api/public/get-release-branches': CacheRequestHandler( - 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( - 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( - 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 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 redirects = { - '/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 3b3fb1168..000000000 --- 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 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 f3e18c49c..000000000 --- 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> 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 noOwnerBuilders = validateOwnership(ciYamlContent, testOwnersContent, unfilteredTargets: true); - return noOwnerBuilders; -} - -/// Local check is based on paths to the local `.ci.yaml` and `TESTOWNERS` files. -List 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 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 main(List 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 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 aaf332da8..000000000 --- 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 6f102209c..000000000 --- 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 ab31d3924..000000000 --- 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 6e74186c0..000000000 --- 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 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 _checkDependencies() async { - if (_ignoreVersion) { - return true; - } - - stdout.writeln('Checking Flutter version via flutter --version'); - final ProcessResult result = await Process.run('flutter', ['--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 _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', - [ - '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 shellCommand(List 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 main(List 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(['$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 b036efd31..000000000 --- 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 a2668516a..000000000 --- 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', ['diff', '--exit-code', path]); - if (gitResult.exitCode != 0) { - final ProcessResult gitDiffOutput = Process.runSync('git', ['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 81860112e..000000000 --- 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 293818125..000000000 --- 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 main() async { - test('validate cocoon ci.yaml generates jspb', () async { - final ProcessResult generateResult = - Process.runSync('dart', ['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 2032f5606..000000000 --- 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 configs = [ - SupportedConfig(RepositorySlug('flutter', 'cocoon'), 'main'), - SupportedConfig(RepositorySlug('flutter', 'engine'), 'main'), - SupportedConfig(RepositorySlug('flutter', 'flutter')), - SupportedConfig(RepositorySlug('flutter', 'packages'), 'main'), -]; - -Future 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 githubBranches = getBranchesForRepository(config.slug); - - final Map validEnabledBranches = {}; - // 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([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 getBranchesForRepository(RepositorySlug slug) { - const ProcessManager processManager = LocalProcessManager(); - final ProcessResult result = - processManager.runSync(['git', 'ls-remote', '--head', 'https://github.com/${slug.fullName}']); - final List lines = (result.stdout as String).split('\n'); - - final List githubBranches = []; - 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 6710c6289..000000000 --- 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 configs = [ - SupportedConfig(RepositorySlug('flutter', 'flutter')), -]; - -Future 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 taskArgs = [config.slug.name, config.branch]; - - const ProcessManager processManager = LocalProcessManager(); - final Process process = await processManager.start( - [dart, taskExecutable, ...taskArgs], - workingDirectory: Directory.current.path, - ); - - final List output = []; - final List error = []; - - process.stdout - .transform(const Utf8Decoder()) - .transform(const LineSplitter()) - .listen((String line) { - stdout.writeln('[STDOUT] $line'); - output.add(line); - }); - - process.stderr - .transform(const Utf8Decoder()) - .transform(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 756415a7b..000000000 --- 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 d0137b3ef..000000000 --- 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 5288e7f66..000000000 --- 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 7880c358f..000000000 --- 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> allCheckRuns( - github.GitHub gitHubClient, - CheckSuiteEvent checkSuiteEvent, - ) async { - final List 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 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> 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 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 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 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 _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 3c2831899..000000000 --- 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 fe0ddc51d..000000000 --- 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 346ccd7b7..000000000 --- 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 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', - { - '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 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 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> getTargetsToRun(Iterable targets, List files) async { - log.info('Getting targets to run from diff.'); - final List targetsToRun = []; - for (Target target in targets) { - final List globs = target.value.runIf; - // Handle case where [Target] initializes empty runif - if (globs.isEmpty) { - // Evaluate run_if_not. - final List 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 insertBigquery(String tableName, Map 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> requestRows = >[]; - - requestRows.add({ - 'json': data, - }); - - // Obtain [rows] to be inserted to [BigQuery]. - final TableDataInsertAllRequest request = TableDataInsertAllRequest.fromJson({'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 validateOwnership(String ciYamlContent, String testOwnersContent, {bool unfilteredTargets = false}) { - final List noOwnerBuilders = []; - 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 { - 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 120f129e0..000000000 --- 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 { - /// Creates a new [AllowedAccount]. - AllowedAccount({ - Key? 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 04d278631..000000000 --- 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 { - Branch({Key? 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 toJson() { - return { - 'id': id, - 'branch': { - '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 25705c175..000000000 --- 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 { - @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 592934ea2..000000000 --- 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 { - Commit({ - Key? 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 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 fromDatastore({ - required DatastoreService datastore, - required Key key, - }) async { - log.fine('Looking up commit by key with id: ${key.id}'); - return datastore.lookupByValue(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 `/`. 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? get key => commit.key; - - @JsonKey(name: 'Checklist') - Map get facade { - return { - 'FlutterRepositoryPath': commit.repository, - 'CreateTimestamp': commit.timestamp, - 'Commit': { - 'Sha': commit.sha, - 'Message': commit.message, - 'Author': { - 'Login': commit.author, - 'avatar_url': commit.authorAvatarUrl, - }, - }, - 'Branch': commit.branch, - }; - } - - /// Serializes this object to a JSON primitive. - Map 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 84969e784..000000000 --- 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 _$SerializableCommitToJson(SerializableCommit instance) => { - '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 63ff03c4d..000000000 --- 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 { - GithubBuildStatusUpdate({ - Key? 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 603407a97..000000000 --- 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 { - GithubGoldStatusUpdate({ - Key? 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 60aba605c..000000000 --- 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?, String> { - const StringKeyConverter(); - - @override - Key? fromJson(String? json) => - (json == null || json.isEmpty) ? null : KeyHelper().decode(json) as Key; - - @override - String toJson(Key? key) => key == null ? '' : KeyHelper().encode(key); -} - -/// A converter for [Key]s encoded as strings. -class IntKeyConverter implements JsonConverter, String> { - const IntKeyConverter(); - - @override - Key fromJson(String json) => KeyHelper().decode(json) as Key; - - @override - String toJson(Key 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 f5d6be432..000000000 --- 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 _defaultTypes = { - 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 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 types; - - /// Encodes the specified [key] as a base-64 encoded protocol buffer - /// representation of the key. - /// - /// See also: - /// - /// * - String encode(Key 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. - /// * - Key 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.emptyKey(Partition(reference.nameSpace.isEmpty ? null : reference.nameSpace)), - (Key previous, Path_Element element) { - final Iterable> entries = - types.entries.where((MapEntry entry) => entry.value.name == element.type); - if (entries.isEmpty) { - throw StateError('Unknown type: ${element.type}'); - } - final MapEntry entry = entries.single; - if (entry.value.idType == IdType.String) { - return previous.append(entry.key, id: element.name); - } else { - return previous.append(entry.key, id: element.id.toInt()); - } - }, - ); - } - - static Map _populateTypes(Set types) { - final Map result = {}; - - for (Type type in types) { - final ClassMirror classMirror = reflectClass(type); - final List 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.unmodifiable(result); - } - - Path _asPath(Key key) { - final List> path = >[]; - for (Key? current = key; current != null && !current.isEmpty; current = current.parent) { - path.insert(0, current); - } - return Path() - ..element.addAll( - path.map((Key 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 57bc0fb49..000000000 --- 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 createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Path_Element getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(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( - 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? 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 createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Path getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Path? _defaultInstance; - - @$pb.TagNumber(1) - $core.List 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(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 createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Reference getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(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 eef0c4675..000000000 --- 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 793f8d92a..000000000 --- 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 fd4acbd34..000000000 --- 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 9a0313d90..000000000 --- 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: -// -// * -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: -// -// * -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 c594176a5..000000000 --- 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 result = Key.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(type, id: key.uid.toInt()); - break; - case pb.Key_Id.name: - result = result.append(type, id: key.name); - break; - case pb.Key_Id.notSet: - result = result.append(type); - break; - } - } - - return KeyWrapper(result); - } - - final Key key; - - pb.RootKey toProto() { - pb.Key? previous; - for (Key? 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 6c2d46ab8..000000000 --- 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: -/// -/// * -/// -/// * `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 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 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 be7b2288a..000000000 --- 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 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 _$ServiceAccountInfoToJson(ServiceAccountInfo instance) => { - '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 c09b2ef53..000000000 --- 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 { - 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 _order = [ - '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 tasks; - - /// Representation of [tasks] used for JSON serialization. - @JsonKey(name: 'Tasks') - List get serializableTasks { - return tasks.map((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 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 tasks = []; - - /// 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.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((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 09d4849d6..000000000 --- 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 _$StageToJson(Stage instance) => { - '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 3bcce62ea..000000000 --- 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 { - /// Creates a new [Task]. - Task({ - Key? 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: [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 fromCommitKey({ - required DatastoreService datastore, - required Key commitKey, - required String name, - }) async { - if (name.isEmpty) { - throw const BadRequestException('task name is null'); - } - final Query query = datastore.db.query(ancestorKey: commitKey)..filter('name =', name); - final List 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 fromKey({ - required DatastoreService datastore, - required Key commitKey, - required int id, - }) { - log.fine('Looking up key...'); - final Key key = Key(commitKey, Task, id); - return datastore.lookupByValue(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 fromDatastore({ - required DatastoreService datastore, - required Key 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 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 commitKey = datastore.db.emptyKey.append(Commit, id: id); - final Commit commit = await datastore.db.lookupValue(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 legalStatusValues = [ - statusCancelled, - statusFailed, - statusInfraFailure, - statusInProgress, - statusNew, - statusSkipped, - statusSucceeded, - ]; - - static const List finishedStatusValues = [ - statusCancelled, - statusFailed, - statusInfraFailure, - statusSkipped, - statusSucceeded, - ]; - - /// The key of the commit that owns this task. - @ModelKeyProperty(propertyName: 'ChecklistKey') - @JsonKey(name: 'ChecklistKey') - @StringKeyConverter() - Key? 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: - /// - /// * - /// - /// 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: - /// * - @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? 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? 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 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 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 completedStatuses = [ - 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 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 targetsToTask(Commit commit, List 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 get key => task.key; - - /// Serializes this object to a JSON primitive. - Map 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 9ef04d6b4..000000000 --- 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 _$TaskToJson(Task instance) => { - '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 _$SerializableTaskToJson(SerializableTask instance) => { - '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 46f18321b..000000000 --- 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 totTargets = totConfig?._targets ?? []; - final List totEnabledTargets = _filterEnabledTargets(totTargets); - totTargetNames = totEnabledTargets.map((Target target) => target.value.name).toList(); - totPostsubmitTargetNames = - totConfig?.postsubmitTargets.map((Target target) => target.value.name).toList() ?? []; - } - - /// List of target names used to filter target from release candidate branches - /// that were already removed from main. - List? totTargetNames; - - /// List of postsubmit target names used to filter target from release candidate branches - /// that were already removed from main. - List? 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 get presubmitTargets { - final Iterable presubmitTargets = - _targets.where((Target target) => target.value.presubmit && !target.value.bringup); - List 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 get postsubmitTargets { - final Iterable postsubmitTargets = _targets.where((Target target) => target.value.postsubmit); - - List 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 get backfillTargets { - final List filteredTargets = []; - for (Target target in postsubmitTargets) { - final Map 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 _filterReleaseBuildTargets(List targets) { - final List results = []; - final bool releaseBranch = branch.contains(RegExp('^flutter-')); - if (!releaseBranch) { - return targets; - } - for (Target target in targets) { - final Map 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 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 getInitialTargets(List targets) { - Iterable 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 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 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 _filterEnabledTargets(Iterable targets) { - final List filteredTargets = []; - - // 1. Add targets with local definition - final Iterable overrideBranchTargets = - targets.where((Target target) => target.value.enabledBranches.isNotEmpty); - final Iterable 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 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 enabledBranches, String branch) { - final List regexes = []; - 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> targetGraph = >{}; - final List exceptions = []; - final Set totTargets = {}; - 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] = []; - // 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 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 exceptions = []; - - /// Decoded will contain a list of maps for the dependencies found. - final List decoded = json.decode(dependencyJsonString) as List; - - for (Map 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 4fa65e13a..000000000 --- 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 iosPlatforms = ['mac_ios', 'mac_arm64_ios']; - - /// Dimension list defined in .ci.yaml. - static List dimensionList = ['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 getDimensions() { - final Map dimensionsMap = {}; - - final Map platformDimensions = _getPlatformDimensions(); - for (String key in platformDimensions.keys) { - final String value = platformDimensions[key].toString(); - dimensionsMap[key] = RequestedDimension(key: key, value: value); - } - - final Map 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 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 get tags { - final Map properties = getProperties(); - return (properties.containsKey('tags')) ? (properties['tags'] as List).map((e) => e as String).toList() : []; - } - - String get getTestName { - final List 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 getProperties() { - final Map platformProperties = _getPlatformProperties(); - final Map properties = _getTargetProperties(); - final Map mergedProperties = {} - ..addAll(platformProperties) - ..addAll(properties); - - final List targetDependencies = []; - if (properties.containsKey('dependencies')) { - final List rawDeps = properties['dependencies'] as List; - final Iterable deps = rawDeps.map((dynamic rawDep) => Dependency.fromJson(rawDep as Object)); - targetDependencies.addAll(deps); - } - final List platformDependencies = []; - if (platformProperties.containsKey('dependencies')) { - final List rawDeps = platformProperties['dependencies'] as List; - final Iterable deps = rawDeps.map((dynamic rawDep) => Dependency.fromJson(rawDep as Object)); - platformDependencies.addAll(deps); - } - // Lookup map to make merging [targetDependencies] and [platformDependencies] simpler. - final Map mergedDependencies = {}; - 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 _getTargetDimensions() { - final Map dimensions = {}; - for (String key in value.dimensions.keys) { - dimensions[key] = _parseProperty(key, value.dimensions[key]!); - } - - return dimensions; - } - - Map _getTargetProperties() { - final Map properties = {}; - for (String key in value.properties.keys) { - properties[key] = _parseProperty(key, value.properties[key]!); - } - - return properties; - } - - Map _getPlatformProperties() { - if (!schedulerConfig.platformProperties.containsKey(getPlatform())) { - return {}; - } - - final Map platformProperties = schedulerConfig.platformProperties[getPlatform()]!.properties; - final Map properties = {}; - for (String key in platformProperties.keys) { - properties[key] = _parseProperty(key, platformProperties[key]!); - } - - return properties; - } - - Map _getPlatformDimensions() { - if (!schedulerConfig.platformProperties.containsKey(getPlatform())) { - return {}; - } - - final Map platformDimensions = schedulerConfig.platformProperties[getPlatform()]!.dimensions; - final Map dimensions = {}; - 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 newLineIssues = ['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 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 map = json as Map; - 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 toJson() { - return { - '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 f10d6ac26..000000000 --- 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>`. -class TagsConverter implements JsonConverter>?, List?> { - const TagsConverter(); - - @override - Map>? fromJson(List? json) { - if (json == null) { - return null; - } - final Map> result = >{}; - for (Map tag in json.cast>()) { - final String? key = tag['key'] as String?; - result[key] ??= []; - result[key]!.add(tag['value'] as String?); - } - return result; - } - - @override - List>? toJson(Map>? object) { - if (object == null) { - return null; - } - if (object.isEmpty) { - return const >>[]; - } - final List> result = >[]; - for (String? key in object.keys) { - if (key == null) { - continue; - } - final List? values = object[key]; - if (values == null) { - continue; - } - for (String? value in values) { - if (value == null) { - continue; - } - result.add({ - '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 { - 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 { - 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 { - 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 { - 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?, String?> { - const NestedJsonConverter(); - - @override - Map? fromJson(String? json) { - if (json == null) { - return null; - } - return convert.json.decode(json) as Map?; - } - - @override - String? toJson(Map? object) { - if (object == null) { - return null; - } - return convert.json.encode(object); - } -} - -const Map _months = { - '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 { - 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 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 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 9b3a16cd1..000000000 --- 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 json) => _$GerritCommitFromJson(json); - - final String? commit; - final String? tree; - final GerritUser? author; - final GerritUser? committer; - final String? message; - - @override - Map 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 json) => _$GerritUserFromJson(json); - - final String? name; - final String? email; - - @GerritDateTimeConverter() - final DateTime? time; - - @override - Map 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 a12615d8b..000000000 --- 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 json) => GerritCommit( - commit: json['commit'] as String?, - tree: json['tree'] as String?, - author: json['author'] == null ? null : GerritUser.fromJson(json['author'] as Map), - committer: json['committer'] == null ? null : GerritUser.fromJson(json['committer'] as Map), - message: json['message'] as String?, - ); - -Map _$GerritCommitToJson(GerritCommit instance) => { - 'commit': instance.commit, - 'tree': instance.tree, - 'author': instance.author, - 'committer': instance.committer, - 'message': instance.message, - }; - -GerritUser _$GerritUserFromJson(Map json) => GerritUser( - name: json['name'] as String?, - email: json['email'] as String?, - time: const GerritDateTimeConverter().fromJson(json['time'] as String?), - ); - -Map _$GerritUserToJson(GerritUser instance) => { - '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 8bd4f3e7e..000000000 --- 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 input) => _$CheckRunEventFromJson(input); - CheckRun? checkRun; - String? action; - User? sender; - Repository? repository; - - Map 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 input) => _$CheckRunFromJson(input); - final int? id; - final String? headSha; - final String? conclusion; - final String? name; - final CheckSuite? checkSuite; - @JsonKey(name: 'pull_requests', defaultValue: []) - final List? pullRequests; - - Map 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 1c8002602..000000000 --- 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 json) => CheckRunEvent( - action: json['action'] as String?, - checkRun: json['check_run'] == null ? null : CheckRun.fromJson(json['check_run'] as Map), - sender: json['sender'] == null ? null : User.fromJson(json['sender'] as Map), - repository: json['repository'] == null ? null : Repository.fromJson(json['repository'] as Map), - ); - -Map _$CheckRunEventToJson(CheckRunEvent instance) => { - 'check_run': instance.checkRun, - 'action': instance.action, - 'sender': instance.sender, - 'repository': instance.repository, - }; - -CheckRun _$CheckRunFromJson(Map json) => CheckRun( - conclusion: json['conclusion'] as String?, - headSha: json['head_sha'] as String?, - id: json['id'] as int?, - pullRequests: (json['pull_requests'] as List?) - ?.map((e) => PullRequest.fromJson(e as Map)) - .toList() ?? - [], - name: json['name'] as String?, - checkSuite: json['check_suite'] == null ? null : CheckSuite.fromJson(json['check_suite'] as Map), - ); - -Map _$CheckRunToJson(CheckRun instance) => { - '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 c3593eb70..000000000 --- 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 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 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 a690c98e1..000000000 --- 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 json) => GrpcStatus( - code: json['code'] as int, - message: json['message'] as String?, - details: json['details'], - ); - -Map _$GrpcStatusToJson(GrpcStatus instance) { - final val = { - '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 6406699b2..000000000 --- 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 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 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 9d8de2992..000000000 --- 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 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 _$TokenInfoToJson(TokenInfo instance) => { - '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 903639eaa..000000000 --- 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 json) => _$BatchRequestFromJson(json); - - /// The batch of [Request]s to make. - final List? requests; - - @override - Map 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 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 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? json) => _$BatchResponseFromJson(json!); - - /// The collected responses from the Batch request. - final List? responses; - - @override - Map 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 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 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 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 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 json) => _$GetBuilderRequestFromJson(json); - - /// The BuildBucket builder ID. - final BuilderId? builderId; - - @override - Map 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 json) => _$BuilderConfigFromJson(json); - - /// The BuildBucket builder ID. - final String? name; - - @override - Map 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? json) => _$BuilderItemFromJson(json!); - - /// The BuildBucket builder ID. - final BuilderId? id; - - /// The BuildBucket builder config. - final BuilderConfig? config; - - @override - Map 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 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 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? json) => _$ListBuildersResponseFromJson(json!); - - /// The [Builders]s returned by the search. - final List? 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 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 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 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 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 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 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>? tags; - - /// Determines whether to include experimental builds in the result. - /// - /// Defaults to false. - final bool? includeExperimental; - - @override - Map 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? json) => _$SearchBuildsResponseFromJson(json!); - - /// The [Build]s returned by the search. - final List? 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 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 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? 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>? 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? 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? exe; - - @override - Map 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? 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>? 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 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 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 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 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 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 json) => _$InputFromJson(json); - - /// The build properties of a build. - final Map? 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 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 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 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 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 's', - /// e.g. '120s' represents 120 seconds. - final String? expiration; - - @override - Map 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 6cb274c00..000000000 --- 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 json) => BatchRequest( - requests: (json['requests'] as List?)?.map((e) => Request.fromJson(e as Map)).toList(), - ); - -Map _$BatchRequestToJson(BatchRequest instance) { - final val = {}; - - void writeNotNull(String key, dynamic value) { - if (value != null) { - val[key] = value; - } - } - - writeNotNull('requests', instance.requests); - return val; -} - -Request _$RequestFromJson(Map json) => Request( - getBuild: json['getBuild'] == null ? null : GetBuildRequest.fromJson(json['getBuild'] as Map), - searchBuilds: json['searchBuilds'] == null - ? null - : SearchBuildsRequest.fromJson(json['searchBuilds'] as Map), - scheduleBuild: json['scheduleBuild'] == null - ? null - : ScheduleBuildRequest.fromJson(json['scheduleBuild'] as Map), - cancelBuild: - json['cancelBuild'] == null ? null : CancelBuildRequest.fromJson(json['cancelBuild'] as Map), - ); - -Map _$RequestToJson(Request instance) { - final val = {}; - - 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 json) => BatchResponse( - responses: - (json['responses'] as List?)?.map((e) => Response.fromJson(e as Map)).toList(), - ); - -Map _$BatchResponseToJson(BatchResponse instance) { - final val = {}; - - void writeNotNull(String key, dynamic value) { - if (value != null) { - val[key] = value; - } - } - - writeNotNull('responses', instance.responses); - return val; -} - -Response _$ResponseFromJson(Map json) => Response( - getBuild: json['getBuild'] == null ? null : Build.fromJson(json['getBuild'] as Map), - searchBuilds: json['searchBuilds'] == null - ? null - : SearchBuildsResponse.fromJson(json['searchBuilds'] as Map), - scheduleBuild: - json['scheduleBuild'] == null ? null : Build.fromJson(json['scheduleBuild'] as Map), - cancelBuild: json['cancelBuild'] == null ? null : Build.fromJson(json['cancelBuild'] as Map), - error: json['error'] == null ? null : GrpcStatus.fromJson(json['error'] as Map), - ); - -Map _$ResponseToJson(Response instance) { - final val = {}; - - 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 json) => GetBuildRequest( - id: json['id'] as String?, - builderId: json['builder'] == null ? null : BuilderId.fromJson(json['builder'] as Map), - buildNumber: json['buildNumber'] as int?, - fields: json['fields'] as String?, - ); - -Map _$GetBuildRequestToJson(GetBuildRequest instance) { - final val = {}; - - 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 json) => GetBuilderRequest( - builderId: json['builderId'] == null ? null : BuilderId.fromJson(json['builderId'] as Map), - ); - -Map _$GetBuilderRequestToJson(GetBuilderRequest instance) { - final val = {}; - - void writeNotNull(String key, dynamic value) { - if (value != null) { - val[key] = value; - } - } - - writeNotNull('builderId', instance.builderId); - return val; -} - -BuilderConfig _$BuilderConfigFromJson(Map json) => BuilderConfig( - name: json['name'] as String?, - ); - -Map _$BuilderConfigToJson(BuilderConfig instance) { - final val = {}; - - void writeNotNull(String key, dynamic value) { - if (value != null) { - val[key] = value; - } - } - - writeNotNull('name', instance.name); - return val; -} - -BuilderItem _$BuilderItemFromJson(Map json) => BuilderItem( - id: json['id'] == null ? null : BuilderId.fromJson(json['id'] as Map), - config: json['config'] == null ? null : BuilderConfig.fromJson(json['config'] as Map), - ); - -Map _$BuilderItemToJson(BuilderItem instance) { - final val = {}; - - 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 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 _$ListBuildersRequestToJson(ListBuildersRequest instance) { - final val = { - '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 json) => ListBuildersResponse( - builders: - (json['builders'] as List?)?.map((e) => BuilderItem.fromJson(e as Map)).toList(), - nextPageToken: json['nextPageToken'] as String?, - ); - -Map _$ListBuildersResponseToJson(ListBuildersResponse instance) { - final val = {}; - - 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 json) { - $checkKeys( - json, - requiredKeys: const ['id', 'summaryMarkdown'], - ); - return CancelBuildRequest( - id: json['id'] as String, - summaryMarkdown: json['summaryMarkdown'] as String, - ); -} - -Map _$CancelBuildRequestToJson(CancelBuildRequest instance) => { - 'id': instance.id, - 'summaryMarkdown': instance.summaryMarkdown, - }; - -SearchBuildsRequest _$SearchBuildsRequestFromJson(Map json) => SearchBuildsRequest( - predicate: BuildPredicate.fromJson(json['predicate'] as Map), - pageSize: json['pageSize'] as int?, - pageToken: json['pageToken'] as String?, - fields: json['fields'] as String?, - ); - -Map _$SearchBuildsRequestToJson(SearchBuildsRequest instance) { - final val = { - '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 json) => BuildPredicate( - builderId: json['builder'] == null ? null : BuilderId.fromJson(json['builder'] as Map), - status: $enumDecodeNullable(_$StatusEnumMap, json['status']), - createdBy: json['createdBy'] as String?, - tags: const TagsConverter().fromJson(json['tags'] as List?), - includeExperimental: json['includeExperimental'] as bool?, - ); - -Map _$BuildPredicateToJson(BuildPredicate instance) { - final val = {}; - - 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 json) => SearchBuildsResponse( - builds: (json['builds'] as List?)?.map((e) => Build.fromJson(e as Map)).toList(), - nextPageToken: json['nextPageToken'] as String?, - ); - -Map _$SearchBuildsResponseToJson(SearchBuildsResponse instance) { - final val = {}; - - 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 json) => ScheduleBuildRequest( - requestId: json['requestId'] as String?, - builderId: BuilderId.fromJson(json['builder'] as Map), - canary: json['canary'] as bool?, - experimental: $enumDecodeNullable(_$TrinaryEnumMap, json['experimental']), - gitilesCommit: - json['gitilesCommit'] == null ? null : GitilesCommit.fromJson(json['gitilesCommit'] as Map), - properties: (json['properties'] as Map?)?.map( - (k, e) => MapEntry(k, e as Object), - ), - dimensions: (json['dimensions'] as List?) - ?.map((e) => RequestedDimension.fromJson(e as Map)) - .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), - fields: json['fields'] as String?, - exe: json['exe'] as Map?, - ); - -Map _$ScheduleBuildRequestToJson(ScheduleBuildRequest instance) { - final val = {}; - - 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 json) => Build( - id: json['id'] as String, - builderId: BuilderId.fromJson(json['builder'] as Map), - 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), - summaryMarkdown: json['summaryMarkdown'] as String?, - critical: $enumDecodeNullable(_$TrinaryEnumMap, json['critical']), - ); - -Map _$BuildToJson(Build instance) { - final val = { - '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 json) => BuilderId( - project: json['project'] as String?, - bucket: json['bucket'] as String?, - builder: json['builder'] as String?, - ); - -Map _$BuilderIdToJson(BuilderId instance) { - final val = {}; - - 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 json) => NotificationConfig( - pubsubTopic: json['pubsubTopic'] as String?, - userData: _$JsonConverterFromJson(json['userData'], const Base64Converter().fromJson), - ); - -Map _$NotificationConfigToJson(NotificationConfig instance) { - final val = {}; - - void writeNotNull(String key, dynamic value) { - if (value != null) { - val[key] = value; - } - } - - writeNotNull('pubsubTopic', instance.pubsubTopic); - writeNotNull('userData', _$JsonConverterToJson(instance.userData, const Base64Converter().toJson)); - return val; -} - -Value? _$JsonConverterFromJson( - Object? json, - Value? Function(Json json) fromJson, -) => - json == null ? null : fromJson(json as Json); - -Json? _$JsonConverterToJson( - Value? value, - Json? Function(Value value) toJson, -) => - value == null ? null : toJson(value); - -Input _$InputFromJson(Map json) => Input( - properties: (json['properties'] as Map?)?.map( - (k, e) => MapEntry(k, e as Object), - ), - gitilesCommit: - json['gitilesCommit'] == null ? null : GitilesCommit.fromJson(json['gitilesCommit'] as Map), - experimental: json['experimental'] as bool?, - ); - -Map _$InputToJson(Input instance) { - final val = {}; - - 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 json) => GitilesCommit( - host: json['host'] as String?, - project: json['project'] as String?, - ref: json['ref'] as String?, - hash: json['id'] as String?, - ); - -Map _$GitilesCommitToJson(GitilesCommit instance) { - final val = {}; - - 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 json) => RequestedDimension( - key: json['key'] as String, - value: json['value'] as String?, - expiration: json['expiration'] as String?, - ); - -Map _$RequestedDimensionToJson(RequestedDimension instance) { - final val = { - '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 d7c150268..000000000 --- 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 json) => _$PushMessageEnvelopeFromJson(json); - - /// The message contents. - final PushMessage? message; - - /// The name of the subscription associated with the delivery. - final String? subscription; - - @override - Map 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 json) => _$PushMessageFromJson(json); - - /// PubSub attributes on the message. - final Map? 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 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); - } on FormatException { - log.info('Result message: $data'); - return BuildPushMessage.fromJson(json.decode(data) as Map); - } - } - - static BuildPushMessage fromJson(Map 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 get userData { - if (rawUserData == null) { - return {}; - } - - try { - return json.decode(rawUserData!) as Map; - } on FormatException { - final Uint8List bytes = base64.decode(rawUserData!); - final String rawJson = String.fromCharCodes(bytes); - if (rawJson.isEmpty) { - return {}; - } - return json.decode(rawJson) as Map; - } - } - - @override - Map 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 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? 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? 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? 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 tagsByName(String prefix) { - return tags! - .where((String tag) => tag.startsWith('$prefix:')) - .map((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 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 963d7b622..000000000 --- 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 json) => PushMessageEnvelope( - message: json['message'] == null ? null : PushMessage.fromJson(json['message'] as Map), - subscription: json['subscription'] as String?, - ); - -Map _$PushMessageEnvelopeToJson(PushMessageEnvelope instance) { - final val = {}; - - 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 json) => PushMessage( - attributes: (json['attributes'] as Map?)?.map( - (k, e) => MapEntry(k, e as String), - ), - data: _$JsonConverterFromJson(json['data'], const Base64Converter().fromJson), - messageId: json['messageId'] as String?, - publishTime: json['publishTime'] as String?, - ); - -Map _$PushMessageToJson(PushMessage instance) { - final val = {}; - - void writeNotNull(String key, dynamic value) { - if (value != null) { - val[key] = value; - } - } - - writeNotNull('attributes', instance.attributes); - writeNotNull('data', _$JsonConverterToJson(instance.data, const Base64Converter().toJson)); - writeNotNull('messageId', instance.messageId); - writeNotNull('publishTime', instance.publishTime); - return val; -} - -Value? _$JsonConverterFromJson( - Object? json, - Value? Function(Json json) fromJson, -) => - json == null ? null : fromJson(json as Json); - -Json? _$JsonConverterToJson( - Value? value, - Json? Function(Value value) toJson, -) => - value == null ? null : toJson(value); - -BuildPushMessage _$BuildPushMessageFromJson(Map json) => BuildPushMessage( - build: json['build'] == null ? null : Build.fromJson(json['build'] as Map), - hostname: json['hostname'] as String?, - userData: json['user_data'] as String?, - ); - -Map _$BuildPushMessageToJson(BuildPushMessage instance) { - final val = {}; - - 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 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?)?.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 _$BuildToJson(Build instance) { - final val = {}; - - 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 dc5a7ebff..000000000 --- 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( - 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 createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuildStatusResponse getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(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 7663ad324..000000000 --- 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 values = [ - 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 768fc3ae6..000000000 --- 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 9115ba506..000000000 --- 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 a562744d5..000000000 --- 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 2f5fafe5c..000000000 --- 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 createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static GithubWebhookMessage getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(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 ef339172e..000000000 --- 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 718d1b946..000000000 --- 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 8e8dfb926..000000000 --- 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 5c28befea..000000000 --- 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 76d26dbc4..000000000 --- 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(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 createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Key getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(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(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 createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static RootKey getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(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 1c5e28e07..000000000 --- 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 315a58e38..000000000 --- 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 92721a995..000000000 --- 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 262418f59..000000000 --- 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 f79eede47..000000000 --- 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 createRepeated() => - $pb.PbList(); - @$core.pragma('dart2js:noInline') - static SchedulerConfig_PlatformProperties getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(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(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? 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 createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static SchedulerConfig getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static SchedulerConfig? _defaultInstance; - - @$pb.TagNumber(1) - $core.List 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( - 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 createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Target getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(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 8e09bd776..000000000 --- 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 values = [ - 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 5e0486076..000000000 --- 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 a16fa8367..000000000 --- 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 a0df48310..000000000 --- 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 platform_properties = 3; - // Next ID: 3 - message PlatformProperties { - // Generic key, value pairs to set platform-wide properties - map 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 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 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 postsubmit_properties = 15; - // Dimensions to configure swarming dimensions of LUCI builds. - map 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 ae7cdee0c..000000000 --- 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 41d64ba53..000000000 --- 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 { - const CheckFlakyBuilders({ - required super.config, - required super.authenticationProvider, - }); - - static int kRecordNumber = 50; - - static final RegExp _issueLinkRegex = RegExp(r'https://github.com/flutter/flutter/issues/(?[0-9]+)'); - - /// Builders that are purposefully marked flaky and should be ignored by this - /// handler. - static const Set ignoredBuilders = { - 'Mac_ios32 flutter_gallery__transition_perf_e2e_ios32', - 'Mac_ios32 native_ui_tests_ios', - }; - - @override - Future 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 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 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 { - '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 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> _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 flakyTargets = targets - .where((dynamic target) => target[kCiYamlTargetIsFlakyKey] == true) - .map((dynamic target) => target as YamlMap?) - .toList(); - final List<_BuilderInfo> result = <_BuilderInfo>[]; - final List lines = content.split('\n'); - final Map 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 _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( - 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 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 7d0d6883b..000000000 --- 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 { - 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 get() async { - checkRequiredQueryParameters([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 b778c5d48..000000000 --- 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 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([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 10ab0bc95..000000000 --- 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 { - const FileFlakyIssueAndPR({ - required super.config, - required super.authenticationProvider, - }); - - static const String kThresholdKey = 'threshold'; - - @override - Future get() async { - final RepositorySlug slug = Config.flutterSlug; - final GithubService gitHub = config.createGithubServiceWithToken(await config.githubOAuthToken); - final BigqueryService bigquery = await config.createBigQueryService(); - final List 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 targets = schedulerConfig.targets; - - final String testOwnerContent = await gitHub.getFileContent(slug, kTestOwnerPath); - final Map nameToExistingIssue = await getExistingIssues(gitHub, slug); - final Map 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({ - 'Status': 'success', - 'NumberOfCreatedIssuesAndPRs': filedIssueAndPRCount, - }); - } - - double get _threshold => double.parse(request!.uri.queryParameters[kThresholdKey]!); - - Future _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( - 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!, [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 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 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 a5f31df81..000000000 --- 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 get issueLabels { - final List labels = [ - 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 get issueLabels { - final List existingLabels = existingIssue.labels.map((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> getExistingIssues(GithubService gitHub, RepositorySlug slug, {String state = 'all'}) async { - final Map nameToExistingIssue = {}; - for (final Issue issue in await gitHub.listIssues(slug, state: state, labels: [kFlakeLabel])) { - if (issue.htmlUrl.contains('pull') == true) { - // For some reason, this github api may also return pull requests. - continue; - } - final Map? 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> getExistingPRs(GithubService gitHub, RepositorySlug slug) async { - final Map nameToExistingPRs = {}; - for (final PullRequest pr in await gitHub.listPullRequests(slug, null)) { - try { - if (pr.body == null) { - continue; - } - final Map? 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 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? 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? _getTags(String? targetName, CiYaml ciYaml, {bool unfilteredTargets = false}) { - final Set 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 ''' -'''; -} - -final RegExp _issueHiddenMetaTagsRegex = - RegExp(r'', 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? retrieveMetaTagsFromContent(String content) { - final RegExpMatch? match = _issueHiddenMetaTagsRegex.firstMatch(content); - if (match == null) { - return null; - } - return jsonDecode(match.namedGroup('meta')!) as Map?; -} - -String _formatRate(double rate) => (rate * 100).toStringAsFixed(2); - -String _issueBuildLinks({String? builder, required List 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 824a0c774..000000000 --- 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 { - 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 get() async { - checkRequiredQueryParameters([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 e4348426e..000000000 --- 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 { - 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 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 8a3adba0d..000000000 --- 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] 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] 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], 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 { - 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 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 greenCommits = await buildStatusService - .retrieveCommitStatus( - limit: commitNumber, - branch: branch, - slug: slug, - ) - .where((CommitStatus status) => everyNonFlakyTaskSucceed(status)) - .map((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 f698e2f48..000000000 --- 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 { - GetReleaseBranches({required super.config, required this.branchService}); - - final BranchService branchService; - - @override - Future get() async { - final GitHub github = await config.createGitHubClient(slug: Config.flutterSlug); - final GithubService githubService = GithubService(github); - final List> 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 0366ab1d6..000000000 --- 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 { - const GetRepos({ - required super.config, - }); - - @override - Future get() async { - final List 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 55f812fc1..000000000 --- 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 { - 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 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 statuses = await buildStatusService - .retrieveCommitStatus( - limit: commitNumber, - timestamp: lastCommitTimestamp, - branch: branch, - slug: slug, - ) - .map( - (CommitStatus status) => SerializableCommitStatus(status, keyHelper.encode(status.commit.key)), - ) - .toList(); - - return Body.forJson({ - 'Statuses': statuses, - }); - } - - Future _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 ownerKey = keyHelper.decode(encodedLastCommitKey) as Key; - final Commit commit = await datastore.db.lookupValue( - 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 toJson() { - return { - 'Checklist': { - '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 05f7762c6..000000000 --- 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 kNotActuallyATest = [ - 'packages/flutter/lib/src/gestures/hit_test.dart', -]; - -/// List of repos that require check for tests. -Set kNeedsTests = { - 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 knownCommentCodeExtensions = { - '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 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 event = jsonDecode(webhook.payload) as Map; - 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 event = jsonDecode(webhook.payload) as Map; - 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); - 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 _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 _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 _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 _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 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 _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 _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 files = gitHubClient.pullRequests.listFiles(slug, pr.number!); - - final Set labels = {}; - 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(['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 _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 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 _applyPackageTestChecks(GitHub gitHubClient, String? eventAction, PullRequest pr) async { - final RepositorySlug slug = pr.base!.repo!.slug(); - final Stream 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 _validateRefs( - GitHub gitHubClient, - PullRequest pr, - ) async { - final RepositorySlug slug = pr.base!.repo!.slug(); - String body; - const List releaseChannels = [ - '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 _alreadyCommented( - GitHub gitHubClient, - PullRequest pr, - String message, - ) async { - final Stream 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 _getPullRequestEvent(String request) async { - try { - return PullRequestEvent.fromJson(json.decode(request) as Map); - } 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 d8cdc0032..000000000 --- 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 { - const GithubRateLimitStatus({ - required super.config, - }); - - @override - Future get() async { - final GithubService githubService = await config.createDefaultGitHubService(); - final Map 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 0d3e1d690..000000000 --- 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 { - 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 secret; - - @override - Future 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 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 _validateRequest( - String? signature, - List requestBody, - ) async { - final String rawKey = await secret; - final List 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 1a70e807f..000000000 --- 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 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 commitKey = Key(Key.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 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 taskKey = Key(commitKey, Task, taskId); - task = await datastore.lookupByValue(taskKey); - } - log.fine('Found $task'); - - if (_shouldUpdateTask(build, task)) { - final String oldTaskStatus = task.status; - task.updateFromBuild(build); - await datastore.insert([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(commitKey); - final CiYaml ciYaml = await scheduler.getCiYaml(commit); - final List 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 63d484106..000000000 --- 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 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 _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 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 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 7c0de9130..000000000 --- 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 { - 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 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 _updatePRs(RepositorySlug slug, String status, DatastoreService datastore) async { - final GitHub github = await config.createGitHubClient(slug: slug); - final List updates = []; - 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 _insertBigquery(RepositorySlug slug, String status, String branch, Config config) async { - const String bigqueryTableName = 'BuildStatus'; - final Map bigqueryData = { - '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 d3fdcd9a6..000000000 --- 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 { - 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 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 _sendStatusUpdates( - DatastoreService datastore, - RepositorySlug slug, - ) async { - final GitHub gitHubClient = await config.createGitHubClient(slug: slug); - final List statusUpdates = []; - 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 incompleteChecks = []; - bool runsGoldenFileTests = false; - final Map data = (await _queryGraphQL( - gitHubGraphQLClient, - slug, - pr.number!, - ))!; - final Map prData = data['repository']['pullRequest'] as Map; - final Map commit = prData['commits']['nodes'].single['commit'] as Map; - List>? checkRuns; - if (commit['checkSuites']['nodes'] != null && (commit['checkSuites']['nodes'] as List).isNotEmpty) { - checkRuns = - (commit['checkSuites']['nodes']?.first['checkRuns']['nodes'] as List).cast>(); - } - checkRuns = checkRuns ?? >[]; - log.fine('This PR has ${checkRuns.length} checks.'); - for (Map 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 _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.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) { - throw const FormatException('Skia gold changelist summary does not match expected format.'); - } - final List patchsets = jsonResponseTriage['patchsets'] as List; - int untriaged = 0; - for (int i = 0; i < patchsets.length; i++) { - final Map patchset = patchsets[i] as Map; - 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 _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!, [ - 'will affect goldens', - ]); - } - - Future _alreadyCommented( - GitHub gitHubClient, - PullRequest pr, - RepositorySlug slug, - String message, - ) async { - final Stream comments = gitHubClient.issues.listCommentsByIssue(slug, pr.number!); - await for (IssueComment comment in comments) { - if (comment.body!.contains(message)) { - return true; - } - } - return false; - } - - Future _isFirstComment( - GitHub gitHubClient, - PullRequest pr, - RepositorySlug slug, - ) async { - final Stream 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?> _queryGraphQL( - GraphQLClient client, - RepositorySlug slug, - int prNumber, -) async { - final QueryResult result = await client.query( - QueryOptions( - document: lang.parseString(pullRequestChecksQuery), - fetchPolicy: FetchPolicy.noCache, - variables: { - '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 6c8ecbe9b..000000000 --- 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 { - const ReadinessCheck({required super.config}); - - @override - Future 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 19e3162bd..000000000 --- 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 { - 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 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([taskKeyParam]); - } else { - // Checks params required when this API is called with curl. - checkRequiredParameters([commitShaParam, taskParam, repoParam]); - slug = RepositorySlug(owner, repo!); - branch ??= Config.defaultBranch(slug); - } - - if (taskName == 'all') { - final Key commitKey = Commit.createKey( - db: datastore.db, - slug: slug!, - gitBranch: branch!, - sha: sha!, - ); - final tasks = await datastore.db.query(ancestorKey: commitKey).run().toList(); - final List> futures = >[]; - 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 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> tags = >{ - 'triggered_by': [email], - 'trigger_type': ['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 _getTaskFromNamedParams({ - required DatastoreService datastore, - String? encodedKey, - String? branch, - String? name, - String? sha, - RepositorySlug? slug, - }) async { - if (encodedKey != null && encodedKey.isNotEmpty) { - final Key key = config.keyHelper.decode(encodedKey) as Key; - return datastore.lookupByValue(key); - } - final Key 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 _getCommitFromTask(DatastoreService datastore, Task task) async { - return (await datastore.lookupByKey(>[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 19829a561..000000000 --- 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 { - 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 get() async { - checkRequiredQueryParameters([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 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 getBuilderList(String builders) { - if (builders.isEmpty) { - return []; - } - 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 0c80599a3..000000000 --- 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 get() async { - final List> futures = >[]; - - for (RepositorySlug slug in config.supportedRepos) { - futures.add(backfillRepository(slug)); - } - - // Process all repos asynchronously - await Future.wait(futures); - - return Body.empty; - } - - Future backfillRepository(RepositorySlug slug) async { - final DatastoreService datastore = datastoreProvider(config.db); - final List tasks = - await (datastore.queryRecentTasks(slug: slug, commitLimit: config.backfillerCommitLimit)).toList(); - - // Construct Task columns to scan for backfilling - final Map> taskMap = >{}; - for (FullTask fullTask in tasks) { - if (taskMap.containsKey(fullTask.task.name)) { - taskMap[fullTask.task.name]!.add(fullTask); - } else { - taskMap[fullTask.task.name!] = [fullTask]; - } - } - - // Check if should be scheduled (there is no yellow runs). Run the most recent gray. - List> backfill = >[]; - for (List taskColumn in taskMap.values) { - final FullTask task = taskColumn.first; - final CiYaml ciYaml = await scheduler.getCiYaml(task.commit); - final List 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, 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((Tuple tuple) => tuple.first.value.name)); - - // Update tasks status as in progress to avoid duplicate scheduling. - final List backfillTasks = backfill.map((Tuple tuple) => tuple.second.task).toList(); - try { - await datastore.withTransaction((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> getFilteredBackfill(List> backfill) { - if (backfill.length <= config.backfillerTargetLimit) { - return backfill; - } - final List> filteredBackfill = >[]; - final List> highPriorityBackfill = - backfill.where((element) => element.third == LuciBuildService.kRerunPriority).toList(); - final List> 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 _scheduleWithRetries(List> backfill) async { - const RetryOptions retryOptions = Config.schedulerRetry; - try { - await retryOptions.retry( - () async { - final List>> tupleLists = - await Future.wait>>(backfillRequestList(backfill)); - if (tupleLists.any((List> 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> _updateBackfill( - List> backfill, - List>> tupleLists, - ) { - final List> updatedBackfill = >[]; - 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>>> backfillRequestList(List> backfill) { - final List>>> futures = >>>[]; - for (Tuple 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 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 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 tasks) { - final List 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 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 874d16d87..000000000 --- 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 post() async { - BatchRequest request; - try { - final String data = message.data!; - Map jsonData; - log.info('rawJson: $data'); - try { - jsonData = jsonDecode(data) as Map; - } on FormatException { - jsonData = json.decode(String.fromCharCodes(base64.decode(data))) as Map; - } - 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 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] of requests that need to be retried. - Future> _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 retry = request.requests ?? []; - 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 852c526b7..000000000 --- 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 { - 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 get() async { - final List> futures = >[]; - 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 _vacuumRepository(gh.RepositorySlug slug) async { - final DatastoreService datastore = datastoreProvider(config.db); - - final List tasks = await datastore.queryRecentTasks(slug: slug).toList(); - final Set tasksToBeReset = {}; - 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 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 cdf3210dc..000000000 --- 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 ' '. - final List 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 lines = match - .namedGroup(kOwnerGroupName)! - .split('\n') - .where((String line) => line.isNotEmpty && !line.startsWith('#')) - .toList(); - - for (final String line in lines) { - final List 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 lines = - match.namedGroup(kOwnerGroupName)!.split('\n').where((String line) => line.contains('@')).toList(); - - for (final String line in lines) { - final List 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 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 commentWords = lines[index].trim().split(' '); - if (testName.contains(commentWords[2])) { - isTestDefined = true; - } - index += 1; - } - if (isTestDefined) { - final List 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 lines = match - .namedGroup(kOwnerGroupName)! - .split('\n') - .where((String line) => line.isNotEmpty && !line.startsWith('#')) - .toList(); - - for (final String line in lines) { - final List words = line.trim().split(' '); - final List 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 d1f2b58e3..000000000 --- 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 { - 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 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 prodBuilderStatisticList = - await bigquery.listBuilderStatistic(kBigQueryProjectId, bucket: 'prod'); - final List stagingBuilderStatisticList = - await bigquery.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging'); - final Map nameToExistingIssue = await getExistingIssues(gitHub, slug, state: 'open'); - await _updateExistingFlakyIssue( - gitHub, - slug, - localCiYaml, - prodBuilderStatisticList: prodBuilderStatisticList, - stagingBuilderStatisticList: stagingBuilderStatisticList, - nameToExistingIssue: nameToExistingIssue, - ); - return Body.forJson(const { - '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 _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 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 _updateExistingFlakyIssue( - GithubService gitHub, - RepositorySlug slug, - CiYaml ciYaml, { - required List prodBuilderStatisticList, - required List stagingBuilderStatisticList, - required Map nameToExistingIssue, - }) async { - final Map builderFlakyMap = {}; - final Map ignoreFlakyMap = {}; - 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 e4e6ed4fd..000000000 --- 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 { - 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 post() async { - checkRequiredParameters([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]); - 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 _getTaskFromNamedParams(DatastoreService datastore) async { - final Key commitKey = await _constructCommitKey(datastore); - - final String? builderName = requestData![builderNameParam] as String?; - final Query query = datastore.db.query(ancestorKey: commitKey); - final List initialTasks = await query.run().toList(); - log.fine('Found ${initialTasks.length} tasks for commit'); - final List tasks = []; - 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> _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 commitKey = datastore.db.emptyKey.append(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(commitKey); - return commit.key; - } -} - -@immutable -class UpdateTaskStatusResponse extends JsonBody { - const UpdateTaskStatusResponse(this.task); - - final Task task; - - @override - Map toJson() { - return { - '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 2f70d0c33..000000000 --- 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 { - 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 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 _vacuumRepository( - gh.RepositorySlug slug, { - DatastoreService? datastore, - required String branch, - }) async { - final GithubService githubService = await config.createGithubService(slug); - final List commits = await _vacuumBranch( - slug, - branch, - datastore: datastore, - githubService: githubService, - ); - await scheduler.addCommits(commits); - } - - Future> _vacuumBranch( - gh.RepositorySlug slug, - String branch, { - DatastoreService? datastore, - required GithubService githubService, - }) async { - List commits = []; - // 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> _toDatastoreCommit( - gh.RepositorySlug slug, - List commits, - DatastoreService? datastore, - String branch, - ) async { - final List recentCommits = []; - for (gh.RepositoryCommit commit in commits) { - final String id = '${slug.fullName}/$branch/${commit.sha}'; - final Key key = datastore!.db.emptyKey.append(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_handling/api_request_handler.dart b/app_dart/lib/src/request_handling/api_request_handler.dart deleted file mode 100644 index b20e17818..000000000 --- a/app_dart/lib/src/request_handling/api_request_handler.dart +++ /dev/null @@ -1,168 +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 'dart:io'; -import 'dart:typed_data'; - -import 'package:meta/meta.dart'; - -import '../model/google/token_info.dart'; -import 'authentication.dart'; -import 'body.dart'; -import 'exceptions.dart'; -import 'request_handler.dart'; - -/// A [RequestHandler] that handles API requests. -/// -/// API requests adhere to a specific contract, as follows: -/// -/// * All requests must be authenticated per [AuthenticationProvider]. -/// -/// `T` is the type of object that is returned as the body of the HTTP response -/// (before serialization). Subclasses whose HTTP responses don't include a -/// body should extend `RequestHandler` and return null in their service -/// handlers ([get] and [post]). -@immutable -abstract class ApiRequestHandler extends RequestHandler { - /// Creates a new [ApiRequestHandler]. - const ApiRequestHandler({ - required super.config, - required this.authenticationProvider, - this.requestBodyValue, - }); - - /// Service responsible for authenticating this [HttpRequest]. - final AuthenticationProvider authenticationProvider; - - /// Throws a [BadRequestException] if any of [requiredParameters] is missing - /// from [requestData]. - @protected - void checkRequiredParameters(List requiredParameters) { - final Iterable missingParams = requiredParameters..removeWhere(requestData!.containsKey); - if (missingParams.isNotEmpty) { - throw BadRequestException('Missing required parameter: ${missingParams.join(', ')}'); - } - } - - /// Gets [TokenInfo] using X-Flutter-IdToken header from an authenticated request. - @protected - Future tokenInfo(HttpRequest request) async { - return authenticationProvider.tokenInfo(request); - } - - /// Throws a [BadRequestException] if any of [requiredQueryParameters] are missing from [requestData]. - @protected - void checkRequiredQueryParameters(List requiredQueryParameters) { - final Iterable missingParams = requiredQueryParameters - ..removeWhere(request!.uri.queryParameters.containsKey); - if (missingParams.isNotEmpty) { - throw BadRequestException('Missing required parameter: ${missingParams.join(', ')}'); - } - } - - /// The authentication context associated with the HTTP request. - /// - /// This is guaranteed to be non-null. If the request was unauthenticated, - /// the request will be denied. - @protected - AuthenticatedContext? get authContext => getValue(ApiKey.authContext); - - /// The raw byte contents of the HTTP request body. - /// - /// If the request did not specify any content in the body, this will be an - /// empty list. It will never be null. - /// - /// See also: - /// - /// * [requestData], which contains the JSON-decoded [Map] of the request - /// body content (if applicable). - @protected - Uint8List? get requestBody => requestBodyValue ?? getValue(ApiKey.requestBody); - - /// Used for injecting [requestBody] in tests. - final Uint8List? requestBodyValue; - - /// The JSON data specified in the HTTP request body. - /// - /// This is guaranteed to be non-null. If the request body was empty, or if - /// it contained non-JSON or binary (non-UTF-8) data, this will be an empty - /// map. - /// - /// See also: - /// - /// * [requestBody], which specifies the raw bytes of the HTTP request body. - @protected - Map? get requestData => getValue>(ApiKey.requestData); - - @override - Future service( - HttpRequest request, { - Future Function(HttpStatusException)? onError, - }) async { - AuthenticatedContext context; - try { - context = await authenticationProvider.authenticate(request); - } on Unauthenticated catch (error) { - final HttpResponse response = request.response; - response - ..statusCode = HttpStatus.unauthorized - ..write(error.message); - await response.flush(); - await response.close(); - return; - } - - List body; - try { - body = await request.expand((List chunk) => chunk).toList(); - } catch (error) { - final HttpResponse response = request.response; - response - ..statusCode = HttpStatus.internalServerError - ..write('$error'); - await response.flush(); - await response.close(); - return; - } - - Map? requestData = const {}; - if (body.isNotEmpty) { - try { - requestData = json.decode(utf8.decode(body)) as Map?; - } on FormatException { - // The HTTP request body is not valid UTF-8 encoded JSON. This is - // allowed; just let [requestData] be null. - } catch (error) { - final HttpResponse response = request.response; - response - ..statusCode = HttpStatus.internalServerError - ..write('$error'); - await response.flush(); - await response.close(); - return; - } - } - - await runZoned>( - () async { - await super.service(request); - }, - zoneValues: , Object?>{ - ApiKey.authContext: context, - ApiKey.requestBody: Uint8List.fromList(body), - ApiKey.requestData: requestData, - }, - ); - } -} - -class ApiKey extends RequestKey { - const ApiKey._(super.name); - - static const ApiKey requestBody = ApiKey._('requestBody'); - static const ApiKey authContext = ApiKey._('authenticatedContext'); - static const ApiKey> requestData = ApiKey>._('requestData'); -} diff --git a/app_dart/lib/src/request_handling/authentication.dart b/app_dart/lib/src/request_handling/authentication.dart deleted file mode 100644 index 21d99ee08..000000000 --- a/app_dart/lib/src/request_handling/authentication.dart +++ /dev/null @@ -1,178 +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 'dart:io'; - -import 'package:appengine/appengine.dart'; -import 'package:gcloud/db.dart'; -import 'package:http/http.dart' as http; -import 'package:meta/meta.dart'; - -import '../../cocoon_service.dart'; -import '../foundation/providers.dart'; -import '../foundation/typedefs.dart'; -import '../model/appengine/allowed_account.dart'; -import '../model/google/token_info.dart'; -import '../service/logging.dart'; -import 'exceptions.dart'; - -/// Class capable of authenticating [HttpRequest]s. -/// -/// There are two types of authentication this class supports: -/// -/// 1. If the request has the `'X-Appengine-Cron'` HTTP header set to "true", -/// then the request will be authenticated as an App Engine cron job. -/// -/// The `'X-Appengine-Cron'` HTTP header is set automatically by App Engine -/// and will be automatically stripped from the request by the App Engine -/// runtime if the request originated from anything other than a cron job. -/// Thus, the header is safe to trust as an authentication indicator. -/// -/// 2. If the request has the `'X-Flutter-IdToken'` HTTP header -/// set to a valid encrypted JWT token, then the request will be authenticated -/// as a user account. -/// -/// @google.com accounts can call APIs using curl and gcloud. -/// E.g. curl '' -H "X-Flutter-IdToken: $(gcloud auth print-identity-token)" -/// -/// User accounts are only authorized if the user is either a "@google.com" -/// account or is an [AllowedAccount] in Cocoon's Datastore. -/// -/// If none of the above authentication methods yield an authenticated -/// request, then the request is unauthenticated, and any call to -/// [authenticate] will throw an [Unauthenticated] exception. -/// -/// See also: -/// -/// * -@immutable -class AuthenticationProvider { - const AuthenticationProvider({ - required this.config, - this.clientContextProvider = Providers.serviceScopeContext, - this.httpClientProvider = Providers.freshHttpClient, - }); - - /// The Cocoon config, guaranteed to be non-null. - final Config config; - - /// Provides the App Engine client context as part of the - /// [AuthenticatedContext]. - /// - /// This is guaranteed to be non-null. - final ClientContextProvider clientContextProvider; - - /// Provides the HTTP client that will be used (if necessary) to verify OAuth - /// ID tokens (JWT tokens). - /// - /// This is guaranteed to be non-null. - final HttpClientProvider httpClientProvider; - - /// Authenticates the specified [request] and returns the associated - /// [AuthenticatedContext]. - /// - /// See the class documentation on [AuthenticationProvider] for a discussion - /// of the different types of authentication that are accepted. - /// - /// This will throw an [Unauthenticated] exception if the request is - /// unauthenticated. - Future authenticate(HttpRequest request) async { - final bool isCron = request.headers.value('X-Appengine-Cron') == 'true'; - final String? idTokenFromHeader = request.headers.value('X-Flutter-IdToken'); - final ClientContext clientContext = clientContextProvider(); - if (isCron) { - // Authenticate cron requests - return AuthenticatedContext(clientContext: clientContext); - } else if (idTokenFromHeader != null) { - TokenInfo token; - try { - token = await tokenInfo(request); - } on Unauthenticated { - token = await tokenInfo(request, tokenType: 'access_token'); - } - return authenticateToken(token, clientContext: clientContext); - } - - throw const Unauthenticated('User is not signed in'); - } - - /// Gets oauth token information. This method requires the token to be stored in - /// X-Flutter-IdToken header. - Future tokenInfo(HttpRequest request, {String tokenType = 'id_token'}) async { - final String? idTokenFromHeader = request.headers.value('X-Flutter-IdToken'); - final http.Client client = httpClientProvider(); - try { - final http.Response verifyTokenResponse = await client.get( - Uri.https( - 'oauth2.googleapis.com', - '/tokeninfo', - { - tokenType: idTokenFromHeader, - }, - ), - ); - - if (verifyTokenResponse.statusCode != HttpStatus.ok) { - /// Google Auth API returns a message in the response body explaining why - /// the request failed. Such as "Invalid Token". - log.fine('Token verification failed: ${verifyTokenResponse.statusCode}; ${verifyTokenResponse.body}'); - throw const Unauthenticated('Invalid ID token'); - } - - try { - return TokenInfo.fromJson(json.decode(verifyTokenResponse.body) as Map); - } on FormatException { - throw InternalServerError('Invalid JSON: "${verifyTokenResponse.body}"'); - } - } finally { - client.close(); - } - } - - Future authenticateToken(TokenInfo token, {required ClientContext clientContext}) async { - // Authenticate as a signed-in Google account via OAuth id token. - final String clientId = await config.oauthClientId; - if (token.audience != clientId && !token.email!.endsWith('@google.com')) { - log.warning('Possible forged token: "${token.audience}" (expected "$clientId")'); - throw const Unauthenticated('Invalid ID token'); - } - - if (token.hostedDomain != 'google.com') { - final bool isAllowed = await _isAllowed(token.email); - if (!isAllowed) { - throw Unauthenticated('${token.email} is not authorized to access the dashboard'); - } - } - return AuthenticatedContext(clientContext: clientContext); - } - - Future _isAllowed(String? email) async { - final Query query = config.db.query() - ..filter('email =', email) - ..limit(20); - - return !(await query.run().isEmpty); - } -} - -/// Class that represents an authenticated request having been made, and any -/// attached metadata to that request. -/// -/// See also: -/// -/// * [AuthenticationProvider] -@immutable -class AuthenticatedContext { - /// Creates a new [AuthenticatedContext]. - const AuthenticatedContext({ - required this.clientContext, - }); - - /// The App Engine [ClientContext] of the current request. - /// - /// This is guaranteed to be non-null. - final ClientContext clientContext; -} diff --git a/app_dart/lib/src/request_handling/body.dart b/app_dart/lib/src/request_handling/body.dart deleted file mode 100644 index dec66ceb8..000000000 --- a/app_dart/lib/src/request_handling/body.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:convert'; -import 'dart:typed_data'; - -import 'package:meta/meta.dart'; - -/// Class that represents an HTTP response body before it has been serialized. -@immutable -abstract class Body { - /// Creates a new [Body]. - const Body(); - - /// Creates a [Body] that serializes the specified String [content]. - factory Body.forString(String content) => _StringBody(content); - - /// Creates a [Body] that passes through the already-serialized [stream]. - factory Body.forStream(Stream stream) => _StreamBody(stream); - - /// Creates a [Body] that serializes the specified JSON [value]. - /// - /// The [value] argument may be any JSON tyope (any scalar value, any object - /// that defines a `toJson()` method that returns a JSON type, or a [List] or - /// [Map] of other JSON types). - factory Body.forJson(dynamic value) => Body.forString(json.encode(value)); - - /// Value indicating that the HTTP response body should be empty. - static const Body empty = _EmptyBody(); - - /// Serializes this response body to bytes. - Stream serialize(); -} - -abstract class JsonBody extends Body { - const JsonBody(); - - @override - Stream serialize() { - final Stream raw = json.encoder.bind(Stream.fromIterable([toJson()])); - return utf8.encoder.bind(raw).cast(); - } - - /// Serializes this response body to a JSON-primitive map. - Map toJson(); -} - -class _EmptyBody extends Body { - const _EmptyBody(); - - @override - Stream serialize() => const Stream.empty(); -} - -class _StringBody extends Body { - const _StringBody(this.content); - - final String content; - - @override - Stream serialize() { - return utf8.encoder.bind(Stream.fromIterable([content])).cast(); - } -} - -class _StreamBody extends Body { - const _StreamBody(this.stream); - - final Stream stream; - - @override - Stream serialize() => stream; -} diff --git a/app_dart/lib/src/request_handling/cache_request_handler.dart b/app_dart/lib/src/request_handling/cache_request_handler.dart deleted file mode 100644 index 878fadd1c..000000000 --- a/app_dart/lib/src/request_handling/cache_request_handler.dart +++ /dev/null @@ -1,78 +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:typed_data'; - -import 'package:meta/meta.dart'; - -import '../request_handling/request_handler.dart'; -import '../service/cache_service.dart'; -import 'body.dart'; - -/// A [RequestHandler] for serving cached responses. -/// -/// High traffic endpoints that have responses that do not change -/// based on request are good for caching. Additionally, saves -/// reading from Datastore which is expensive both timewise and monetarily. -@immutable -class CacheRequestHandler extends RequestHandler { - /// Creates a new [CacheRequestHandler]. - const CacheRequestHandler({ - required this.delegate, - required super.config, - required this.cache, - this.ttl = const Duration(minutes: 1), - }); - - /// [RequestHandler] to fallback on for cache misses. - final RequestHandler delegate; - - final CacheService cache; - - /// The time to live for the response stored in the cache. - final Duration ttl; - - @visibleForTesting - static const String responseSubcacheName = 'response'; - - @visibleForTesting - static const String flushCacheQueryParam = 'flushCache'; - - /// Services a cached request. - /// - /// Given the query param [flushCacheQueryParam]=true, it will purge the - /// response from the cache before getting it to set the cached response - /// to the latest information. - @override - Future get() async { - final String responseKey = '${request!.uri.path}:${request!.uri.query}'; - - if (request!.uri.queryParameters[flushCacheQueryParam] == 'true') { - await cache.purge(responseSubcacheName, responseKey); - } - - final Uint8List? cachedResponse = await cache.getOrCreateWithLocking( - responseSubcacheName, - responseKey, - createFn: () => getBodyBytesFromDelegate(delegate), - ttl: ttl, - ); - - return Body.forStream(Stream.value(cachedResponse)) as T; - } - - /// Get a Uint8List that contains the bytes of the response from [delegate] - /// so it can be stored in [cache]. - Future getBodyBytesFromDelegate(RequestHandler delegate) async { - final Body body = await delegate.get(); - - // Body only offers getting a Stream since it just sends - // the data out usually to a client. In this case, we want to store - // the bytes in the cache which requires several conversions to get a - // Uint8List that contains the bytes of the response. - final List rawBytes = await body.serialize().expand((Uint8List? chunk) => chunk!).toList(); - return Uint8List.fromList(rawBytes); - } -} diff --git a/app_dart/lib/src/request_handling/exceptions.dart b/app_dart/lib/src/request_handling/exceptions.dart deleted file mode 100644 index e40c436cb..000000000 --- a/app_dart/lib/src/request_handling/exceptions.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 'dart:io'; - -/// An exception that may be thrown by a [RequestHandler] to trigger an error -/// HTTP response. -class HttpStatusException implements Exception { - /// Creates a new [HttpStatusException]. - const HttpStatusException(this.statusCode, this.message); - - /// The HTTP status code to return to the issuer. - final int statusCode; - - /// The message to show to the issuer to explain the error. - final String message; - - @override - String toString() => 'HTTP $statusCode: $message'; -} - -/// Exception that will trigger an HTTP 400 bad request. -class BadRequestException extends HttpStatusException { - const BadRequestException([String message = 'Bad request']) : super(HttpStatus.badRequest, message); -} - -/// Exception that will trigger an HTTP 404 not found -class NotFoundException extends HttpStatusException { - const NotFoundException(String missing) : super(HttpStatus.notFound, 'Not found: $missing'); -} - -/// Exception that will trigger an HTTP 405 method not allowed. -class MethodNotAllowed extends HttpStatusException { - const MethodNotAllowed(String method) : super(HttpStatus.methodNotAllowed, 'Unsupported method: $method'); -} - -/// Exception that will trigger an HTTP 409 conflict. -class ConflictException extends HttpStatusException { - const ConflictException([String message = 'Request conflict with server state']) - : super(HttpStatus.conflict, message); -} - -/// Exception that will trigger an HTTP 500 internal server error. -class InternalServerError extends HttpStatusException { - const InternalServerError([String message = 'Internal server error']) - : super(HttpStatus.internalServerError, message); -} - -/// Exception that will trigger an HTTP 401 not authorized. -class Unauthorized extends HttpStatusException { - const Unauthorized([String message = 'Unauthorized']) : super(HttpStatus.unauthorized, message); -} - -/// Exception that will trigger an HTTP 403 forbidden. -class Forbidden extends HttpStatusException { - const Forbidden([String message = 'Forbidden']) : super(HttpStatus.forbidden, message); -} - -/// Exception thrown when attempting to authenticate a request that cannot be -/// authenticated. -class Unauthenticated implements Exception { - const Unauthenticated(this.message); - - final String message; - - @override - String toString() => 'Unauthenticated: $message'; -} diff --git a/app_dart/lib/src/request_handling/no_auth_request_handler.dart b/app_dart/lib/src/request_handling/no_auth_request_handler.dart deleted file mode 100644 index 92a52442d..000000000 --- a/app_dart/lib/src/request_handling/no_auth_request_handler.dart +++ /dev/null @@ -1,119 +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 'dart:io'; -import 'dart:typed_data'; - -import 'package:meta/meta.dart'; - -import 'body.dart'; -import 'exceptions.dart'; -import 'request_handler.dart'; - -/// A [RequestHandler] that handles requests with no authentication. -/// -/// No Auth requests enables [requestData] -/// -/// `T` is the type of object that is returned as the body of the HTTP response -/// (before serialization). Subclasses whose HTTP responses don't include a -/// body should extend `RequestHandler` and return null in their service -/// handlers ([get] and [post]). -@immutable -abstract class NoAuthRequestHandler extends RequestHandler { - /// Creates a new [NoAuthRequestHandler]. - const NoAuthRequestHandler({ - required super.config, - }); - - /// Throws a [BadRequestException] if any of [requiredParameters] is missing - /// from [requestData]. - @protected - void checkRequiredParameters(List requiredParameters) { - final Iterable missingParams = requiredParameters..removeWhere(requestData!.containsKey); - if (missingParams.isNotEmpty) { - throw BadRequestException('Missing required parameter: ${missingParams.join(', ')}'); - } - } - - /// The raw byte contents of the HTTP request body. - /// - /// If the request did not specify any content in the body, this will be an - /// empty list. It will never be null. - /// - /// See also: - /// - /// * [requestData], which contains the JSON-decoded [Map] of the request - /// body content (if applicable). - @protected - Uint8List? get requestBody => getValue(NoAuthKey.requestBody); - - /// The JSON data specified in the HTTP request body. - /// - /// This is guaranteed to be non-null. If the request body was empty, or if - /// it contained non-JSON or binary (non-UTF-8) data, this will be an empty - /// map. - /// - /// See also: - /// - /// * [requestBody], which specifies the raw bytes of the HTTP request body. - @protected - Map? get requestData => getValue>(NoAuthKey.requestData); - - @override - Future service( - HttpRequest request, { - Future Function(HttpStatusException)? onError, - }) async { - List body; - try { - body = await request.expand((List chunk) => chunk).toList(); - } catch (error) { - final HttpResponse response = request.response; - response - ..statusCode = HttpStatus.internalServerError - ..write('$error'); - await response.flush(); - await response.close(); - return; - } - - Map? requestData = const {}; - if (body.isNotEmpty) { - try { - requestData = json.decode(utf8.decode(body)) as Map?; - } on FormatException { - // The HTTP request body is not valid UTF-8 encoded JSON. This is - // allowed; just let [requestData] be null. - } catch (error) { - final HttpResponse response = request.response; - response - ..statusCode = HttpStatus.internalServerError - ..write('$error'); - await response.flush(); - await response.close(); - return; - } - } - - await runZoned>( - () async { - await super.service(request); - }, - zoneValues: , Object?>{ - NoAuthKey.requestBody: Uint8List.fromList(body), - NoAuthKey.requestData: requestData, - }, - ); - } -} - -@visibleForTesting -class NoAuthKey extends RequestKey { - const NoAuthKey._(super.name); - - static const NoAuthKey requestBody = NoAuthKey._('requestBody'); - static const NoAuthKey> requestData = NoAuthKey>._('requestData'); -} diff --git a/app_dart/lib/src/request_handling/pubsub.dart b/app_dart/lib/src/request_handling/pubsub.dart deleted file mode 100644 index a608c5efa..000000000 --- a/app_dart/lib/src/request_handling/pubsub.dart +++ /dev/null @@ -1,43 +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:googleapis/pubsub/v1.dart'; -import 'package:googleapis_auth/auth_io.dart'; -import 'package:http/http.dart'; - -import '../foundation/providers.dart'; -import '../foundation/typedefs.dart'; -import '../service/logging.dart'; - -/// Service class for interacting with PubSub. -class PubSub { - const PubSub({ - this.httpClientProvider = Providers.freshHttpClient, - }); - - final HttpClientProvider httpClientProvider; - - Future> publish(String topic, dynamic json) async { - final Client httpClient = await clientViaApplicationDefaultCredentials( - scopes: [ - PubsubApi.pubsubScope, - ], - ); - final PubsubApi pubsubApi = PubsubApi(httpClient); - final String messageData = jsonEncode(json); - final List messageBytes = utf8.encode(messageData); - final String messageBase64 = base64Encode(messageBytes); - final PublishRequest request = PublishRequest( - messages: [ - PubsubMessage(data: messageBase64), - ], - ); - final String fullTopicName = 'projects/flutter-dashboard/topics/$topic'; - final PublishResponse response = await pubsubApi.projects.topics.publish(request, fullTopicName); - log.info('pubsub response messageId=${response.messageIds}'); - return response.messageIds!; - } -} diff --git a/app_dart/lib/src/request_handling/pubsub_authentication.dart b/app_dart/lib/src/request_handling/pubsub_authentication.dart deleted file mode 100644 index fc9f90b90..000000000 --- a/app_dart/lib/src/request_handling/pubsub_authentication.dart +++ /dev/null @@ -1,80 +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:appengine/appengine.dart'; -import 'package:googleapis/oauth2/v2.dart'; -import 'package:http/http.dart'; -import 'package:meta/meta.dart'; - -import '../../cocoon_service.dart'; -import '../foundation/providers.dart'; -import '../service/logging.dart'; -import 'exceptions.dart'; - -/// Class capable of authenticating [HttpRequest]s for PubSub messages. -/// -/// This class implements an ACL on a [RequestHandler] to ensure only automated -/// systems can access the endpoints. -/// -/// If the request has [HttpHeaders.authorizationHeader], the token -/// will be authenticated as a LUCI bot. This token is validated against -/// Google Auth APIs. -/// -/// If there is no token, or it cannot be authenticated, [Unauthenticated] is thrown. -@immutable -class PubsubAuthenticationProvider extends AuthenticationProvider { - const PubsubAuthenticationProvider({ - required super.config, - super.clientContextProvider = Providers.serviceScopeContext, - super.httpClientProvider = Providers.freshHttpClient, - }); - - static const String kBearerTokenPrefix = 'Bearer '; - - /// Authenticates the specified [request] and returns the associated - /// [AuthenticatedContext]. - /// - /// See the class documentation on [AuthenticationProvider] for a discussion - /// of the different types of authentication that are accepted. - /// - /// This will throw an [Unauthenticated] exception if the request is - /// unauthenticated. - @override - Future authenticate(HttpRequest request) async { - final String? idToken = request.headers.value(HttpHeaders.authorizationHeader); - - final ClientContext clientContext = clientContextProvider(); - - log.fine('Authenticating as pubsub message'); - return authenticateIdToken(idToken, clientContext: clientContext); - } - - /// Authenticate [idToken] against Google OAuth 2 API. - Future authenticateIdToken( - String? idToken, { - required ClientContext clientContext, - }) async { - if (idToken == null || !idToken.startsWith(kBearerTokenPrefix)) { - throw const Unauthenticated('${HttpHeaders.authorizationHeader} is null'); - } - final Client client = httpClientProvider(); - final Oauth2Api oauth2api = Oauth2Api(client); - - // Get token from Google oauth - final Tokeninfo info = await oauth2api.tokeninfo( - idToken: idToken.substring(kBearerTokenPrefix.length), - ); - if (info.expiresIn == null || info.expiresIn! < 1) { - throw const Unauthenticated('Token is expired'); - } - - if (Config.allowedPubsubServiceAccounts.contains(info.email)) { - return AuthenticatedContext(clientContext: clientContext); - } - throw Unauthenticated('${info.email} is not in allowedPubsubServiceAccounts'); - } -} diff --git a/app_dart/lib/src/request_handling/request_handler.dart b/app_dart/lib/src/request_handling/request_handler.dart deleted file mode 100644 index 2577b4c6b..000000000 --- a/app_dart/lib/src/request_handling/request_handler.dart +++ /dev/null @@ -1,172 +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:http/http.dart' as http; -import 'package:meta/meta.dart'; - -import '../service/config.dart'; -import '../service/logging.dart'; - -import 'body.dart'; -import 'exceptions.dart'; - -/// A class that services HTTP requests and returns HTTP responses. -/// -/// `T` is the type of object that is returned as the body of the HTTP response -/// (before serialization). Subclasses whose HTTP responses don't include a -/// body should extend `RequestHandler` and return null in their service -/// handlers ([get] and [post]). - -abstract class RequestHandler { - /// Creates a new [RequestHandler]. - const RequestHandler({ - required this.config, - }); - - /// The global configuration of this AppEngine server. - final Config config; - - /// Services an HTTP request. - /// - /// Subclasses should generally not override this method. Instead, they - /// should override one of [get] or [post], depending on which methods - /// they support. - Future service( - HttpRequest request, { - Future Function(HttpStatusException)? onError, - }) { - return runZoned>( - () async { - final HttpResponse response = request.response; - try { - try { - T body; - switch (request.method) { - case 'GET': - body = await get(); - break; - case 'POST': - body = await post(); - break; - default: - throw MethodNotAllowed(request.method); - } - await _respond(body: body); - httpClient?.close(); - return; - } on HttpStatusException { - rethrow; - } catch (error, stackTrace) { - log.severe('$error\n$stackTrace'); - throw InternalServerError('$error\n$stackTrace'); - } - } on HttpStatusException catch (error) { - if (onError != null) { - await onError(error); - } - response - ..statusCode = error.statusCode - ..write(error.message); - await response.flush(); - await response.close(); - return; - } - }, - zoneValues: , Object>{ - RequestKey.request: request, - RequestKey.response: request.response, - RequestKey.httpClient: httpClient ?? http.Client(), - }, - ); - } - - /// Responds (using [response]) with the specified [status] and optional - /// [body]. - /// - /// Returns a future that completes when [response] has been closed. - Future _respond({int status = HttpStatus.ok, Body body = Body.empty}) async { - response!.statusCode = status; - await response!.addStream(body.serialize().cast>()); - await response!.flush(); - await response!.close(); - } - - /// Gets the value associated with the specified [key] in the request - /// context. - /// - /// Concrete subclasses should not call this directly. Instead, they should - /// access the getters that are tied to specific keys, such as [request] - /// and [response]. - /// - /// If this is called outside the context of an HTTP request, this will - /// throw a [StateError]. - @protected - U? getValue(RequestKey key, {bool allowNull = false}) { - final U? value = Zone.current[key] as U?; - if (!allowNull && value == null) { - throw StateError('Attempt to access ${key.name} while not in a request context'); - } - return value; - } - - /// Gets the current [HttpRequest]. - /// - /// If this is called outside the context of an HTTP request, this will - /// throw a [StateError]. - @protected - HttpRequest? get request => getValue(RequestKey.request); - - /// Gets the current [HttpResponse]. - /// - /// If this is called outside the context of an HTTP request, this will - /// throw a [StateError]. - @protected - HttpResponse? get response => getValue(RequestKey.response); - - /// Services an HTTP GET. - /// - /// Subclasses should override this method if they support GET requests. - /// The default implementation will respond with HTTP 405 method not allowed. - @protected - Future get() async { - throw const MethodNotAllowed('GET'); - } - - /// Services an HTTP POST. - /// - /// Subclasses should override this method if they support POST requests. - /// The default implementation will respond with HTTP 405 method not allowed. - @protected - Future post() async { - throw const MethodNotAllowed('POST'); - } - - /// The package:http Client to use for googleapis requests. - @protected - http.Client? get httpClient => getValue( - RequestKey.httpClient, - allowNull: true, - ); -} - -/// A key that can be used to index a value within the request context. -/// -/// Subclasses will only need to deal directly with this class if they add -/// their own request context values. -@protected -class RequestKey { - const RequestKey(this.name); - - final String name; - - static const RequestKey request = RequestKey('request'); - static const RequestKey response = RequestKey('response'); - static const RequestKey httpClient = RequestKey('httpClient'); - - @override - String toString() => '$runtimeType($name)'; -} diff --git a/app_dart/lib/src/request_handling/static_file_handler.dart b/app_dart/lib/src/request_handling/static_file_handler.dart deleted file mode 100644 index 8eef08c3a..000000000 --- a/app_dart/lib/src/request_handling/static_file_handler.dart +++ /dev/null @@ -1,61 +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' show ContentType, HttpResponse; -import 'dart:typed_data'; - -import 'package:file/file.dart'; -import 'package:file/local.dart'; -import 'package:meta/meta.dart'; -import 'package:mime/mime.dart'; -import 'package:path/path.dart' as path; - -import '../../cocoon_service.dart'; -import 'exceptions.dart'; - -/// A class based on [RequestHandler] for serving static files. -@immutable -class StaticFileHandler extends RequestHandler { - /// Creates a new [StaticFileHandler]. - const StaticFileHandler( - this.filePath, { - required super.config, - this.fs = const LocalFileSystem(), - }); - - /// The current [FileSystem] to retrieve files from. - final FileSystem fs; - - /// The location of the static file to serve to the client. - final String filePath; - - /// Services an HTTP GET Request for static files. - @override - Future get() async { - final HttpResponse response = request!.response; - - /// The map of mimeTypes not found in [mime] package. - final Map mimeTypeMap = { - '.map': 'application/json', - '': 'text/plain', - '.smcbin': 'application/octet-stream', - }; - - final String resultPath = filePath == '/' ? '/index.html' : filePath; - - /// The file path in app_dart to the files to serve - const String basePath = 'build/web'; - final File file = fs.file('$basePath$resultPath'); - if (file.existsSync()) { - final String mimeType = mimeTypeMap.containsKey(path.extension(file.path)) - ? mimeTypeMap[path.extension(file.path)]! - : lookupMimeType(resultPath)!; - response.headers.contentType = ContentType.parse(mimeType); - return Body.forStream(file.openRead().cast()); - } else { - throw NotFoundException(resultPath); - } - } -} diff --git a/app_dart/lib/src/request_handling/subscription_handler.dart b/app_dart/lib/src/request_handling/subscription_handler.dart deleted file mode 100644 index b7cea2e56..000000000 --- a/app_dart/lib/src/request_handling/subscription_handler.dart +++ /dev/null @@ -1,160 +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 'dart:io'; -import 'dart:typed_data'; -import 'package:meta/meta.dart'; - -import '../model/luci/push_message.dart'; -import '../service/cache_service.dart'; -import '../service/logging.dart'; -import 'api_request_handler.dart'; -import 'authentication.dart'; -import 'body.dart'; -import 'exceptions.dart'; -import 'pubsub_authentication.dart'; -import 'request_handler.dart'; - -/// An [ApiRequestHandler] that handles PubSub subscription messages. -/// -/// Messages adhere to a specific contract, as follows: -/// -/// * All requests must be authenticated per [AuthenticationProvider]. -/// * Request body is passed following the format of [PushMessageEnvelope]. -@immutable -abstract class SubscriptionHandler extends RequestHandler { - /// Creates a new [SubscriptionHandler]. - const SubscriptionHandler({ - required this.cache, - required super.config, - this.authProvider, - required this.subscriptionName, - }); - - final CacheService cache; - - /// Service responsible for authenticating this [HttpRequest]. - final AuthenticationProvider? authProvider; - - /// Unique identifier of the PubSub subscription in this cloud project. - final String subscriptionName; - - /// The authentication context associated with the HTTP request. - /// - /// This is guaranteed to be non-null. If the request was unauthenticated, - /// the request will be denied. - @protected - AuthenticatedContext get authContext => getValue(ApiKey.authContext)!; - - /// The [PushMessage] from this [HttpRequest]. - @protected - PushMessage get message => getValue(PubSubKey.message)!; - - @override - Future service( - HttpRequest request, { - Future Function(HttpStatusException)? onError, - }) async { - AuthenticatedContext authContext; - final AuthenticationProvider auth = authProvider ?? PubsubAuthenticationProvider(config: config); - try { - authContext = await auth.authenticate(request); - } on Unauthenticated catch (error) { - final HttpResponse response = request.response; - response - ..statusCode = HttpStatus.unauthorized - ..write(error.message); - await response.flush(); - await response.close(); - return; - } - - List body; - try { - body = await request.expand((List chunk) => chunk).toList(); - } catch (error) { - final HttpResponse response = request.response; - response - ..statusCode = HttpStatus.internalServerError - ..write('$error'); - await response.flush(); - await response.close(); - return; - } - - log.fine('Request body: ${utf8.decode(body)}'); - PushMessageEnvelope? envelope; - if (body.isNotEmpty) { - try { - final Map json = jsonDecode(utf8.decode(body)) as Map; - envelope = PushMessageEnvelope.fromJson(json); - } catch (error) { - final HttpResponse response = request.response; - response - ..statusCode = HttpStatus.internalServerError - ..write('$error'); - await response.flush(); - await response.close(); - return; - } - } - - if (envelope == null) { - throw const BadRequestException('Failed to get message'); - } - log.finer(envelope.toJson()); - log.fine('PubsubMessage publishTime: ${envelope.message!.publishTime}'); - - final String messageId = envelope.message!.messageId!; - - final Uint8List? messageLock = await cache.getOrCreate( - subscriptionName, - messageId, - createFn: null, - ); - if (messageLock != null) { - // No-op - There's already a write lock for this message - final HttpResponse response = request.response - ..statusCode = HttpStatus.ok - ..write('$messageId was already processed'); - await response.flush(); - await response.close(); - return; - } - - // Create a write lock in the cache to ensure requests are only processed once - final Uint8List lockValue = Uint8List.fromList('l'.codeUnits); - await cache.set( - subscriptionName, - messageId, - lockValue, - ttl: const Duration(days: 1), - ); - - log.info('Processing message $messageId'); - await runZoned>( - () async => super.service( - request, - onError: (HttpStatusException exception) async { - log.warning('Failed to process $message. (${exception.statusCode}) ${exception.message}'); - await cache.purge(subscriptionName, messageId); - log.info('Purged write lock from cache'); - }, - ), - zoneValues: , Object?>{ - PubSubKey.message: envelope.message, - ApiKey.authContext: authContext, - }, - ); - } -} - -@visibleForTesting -class PubSubKey extends RequestKey { - const PubSubKey._(super.name); - - static const PubSubKey message = PubSubKey._('message'); -} diff --git a/app_dart/lib/src/request_handling/swarming_authentication.dart b/app_dart/lib/src/request_handling/swarming_authentication.dart deleted file mode 100644 index 8d77f9f2a..000000000 --- a/app_dart/lib/src/request_handling/swarming_authentication.dart +++ /dev/null @@ -1,123 +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 'dart:io'; - -import 'package:appengine/appengine.dart'; -import 'package:http/http.dart'; -import 'package:meta/meta.dart'; - -import '../../cocoon_service.dart'; -import '../foundation/providers.dart'; -import '../model/google/token_info.dart'; -import '../service/logging.dart'; -import 'exceptions.dart'; - -/// Class capable of authenticating [HttpRequest]s for infra endpoints. -/// -/// This class implements an ACL on a [RequestHandler] to ensure only automated -/// systems can access the endpoints. -/// -/// If the request has the `Service-Account-Token` HTTP header, the token -/// will be authenticated as a LUCI bot. This token is validated against -/// Google Auth APIs. -/// -/// If none of the above authentication methods yield an authenticated -/// request, then the request is unauthenticated, and any call to -/// [authenticate] will throw an [Unauthenticated] exception. -@immutable -class SwarmingAuthenticationProvider extends AuthenticationProvider { - const SwarmingAuthenticationProvider({ - required super.config, - super.clientContextProvider = Providers.serviceScopeContext, - super.httpClientProvider = Providers.freshHttpClient, - }); - - /// Name of the header that LUCI requests will put their service account token. - static const String kSwarmingTokenHeader = 'Service-Account-Token'; - - /// Authenticates the specified [request] and returns the associated - /// [AuthenticatedContext]. - /// - /// See the class documentation on [AuthenticationProvider] for a discussion - /// of the different types of authentication that are accepted. - /// - /// This will throw an [Unauthenticated] exception if the request is - /// unauthenticated. - @override - Future authenticate(HttpRequest request) async { - final String? swarmingToken = request.headers.value(kSwarmingTokenHeader); - - final ClientContext clientContext = clientContextProvider(); - - if (swarmingToken != null) { - log.fine('Authenticating as swarming task'); - return authenticateAccessToken(swarmingToken, clientContext: clientContext); - } - - throw const Unauthenticated('Request rejected due to not from LUCI'); - } - - /// Authenticate [accessToken] against Google OAuth 2 API. - /// - /// Access tokens are the legacy authentication strategy for Google OAuth, where ID tokens - /// are the new technique to use. LUCI auth only generates access tokens, and must be - /// validated against a different endpoint. We only authenticate access tokens - /// if they belong to a LUCI prod service account. - /// - /// If LUCI auth adds id tokens, we can switch to that and remove this. - Future authenticateAccessToken( - String accessToken, { - required ClientContext clientContext, - }) async { - // Authenticate as a signed-in Google account via OAuth id token. - final Client client = httpClientProvider(); - try { - log.fine('Sending token request to Google OAuth'); - final Response verifyTokenResponse = await client.get( - Uri.https( - 'oauth2.googleapis.com', - '/tokeninfo', - { - 'access_token': accessToken, - }, - ), - ); - - if (verifyTokenResponse.statusCode != HttpStatus.ok) { - /// Google Auth API returns a message in the response body explaining why - /// the request failed. Such as "Invalid Token". - final String body = verifyTokenResponse.body; - log.warning('Token verification failed: ${verifyTokenResponse.statusCode}; $body'); - throw const Unauthenticated('Invalid access token'); - } - - TokenInfo token; - try { - token = TokenInfo.fromJson(json.decode(verifyTokenResponse.body) as Map); - } on FormatException { - log.warning('Failed to decode token JSON: ${verifyTokenResponse.body}'); - throw InternalServerError('Invalid JSON: "${verifyTokenResponse.body}"'); - } - - // Update is from Flutter LUCI builds - if (token.email == Config.luciProdAccount) { - return AuthenticatedContext(clientContext: clientContext); - } - - if (token.email == Config.frobAccount) { - log.fine('Authenticating as FRoB request'); - return AuthenticatedContext(clientContext: clientContext); - } - - log.fine(verifyTokenResponse.body); - log.warning('${token.email} is not allowed'); - throw Unauthenticated('${token.email} is not allowed'); - } finally { - client.close(); - } - } -} diff --git a/app_dart/lib/src/service/access_client_provider.dart b/app_dart/lib/src/service/access_client_provider.dart deleted file mode 100644 index fb259d1c7..000000000 --- a/app_dart/lib/src/service/access_client_provider.dart +++ /dev/null @@ -1,15 +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/auth_io.dart'; -import 'package:http/http.dart'; - -class AccessClientProvider { - /// Returns an OAuth 2.0 authenticated access client for the device lab service account. - Future createAccessClient({ - List scopes = const ['https://www.googleapis.com/auth/cloud-platform'], - }) async { - return clientViaApplicationDefaultCredentials(scopes: scopes); - } -} diff --git a/app_dart/lib/src/service/access_token_provider.dart b/app_dart/lib/src/service/access_token_provider.dart deleted file mode 100644 index 69f262157..000000000 --- a/app_dart/lib/src/service/access_token_provider.dart +++ /dev/null @@ -1,36 +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/auth_io.dart'; -import 'package:http/http.dart' as http; - -import 'config.dart'; - -/// Function signature for a [TaskService] provider. -typedef AccessTokenServiceProvider = AccessTokenService Function(Config config); - -/// A provider for Oauth2 tokens from Service Account JSON. -class AccessTokenService { - /// Creates a new Access Token provider. - const AccessTokenService(this.config); - - /// The Cocoon configuration. - final Config config; - - /// Creates and returns a [AccessTokenService] using a [Config] object. - static AccessTokenService defaultProvider(Config config) { - return AccessTokenService(config); - } - - /// Returns an OAuth 2.0 access token for the device lab service account. - Future createAccessToken() async { - final http.Client httpClient = http.Client(); - try { - final AccessCredentials credentials = await obtainAccessCredentialsViaMetadataServer(httpClient); - return credentials.accessToken; - } finally { - httpClient.close(); - } - } -} diff --git a/app_dart/lib/src/service/bigquery.dart b/app_dart/lib/src/service/bigquery.dart deleted file mode 100644 index 2b3287011..000000000 --- a/app_dart/lib/src/service/bigquery.dart +++ /dev/null @@ -1,248 +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:googleapis/bigquery/v2.dart'; -import 'package:http/http.dart'; - -import 'access_client_provider.dart'; - -/// The sql query to query the build statistic from the -/// `flutter-dashboard.datasite.luci_prod_build_status`. -/// -/// The schema of the `luci_prod_build_status` table: -/// time TIMESTAMP -/// date DATE -/// sha STRING -/// flaky_builds STRING -/// succeeded_builds STRING -/// branch STRING -/// device_os STRING -/// pool STRING -/// repo STRING -/// builder_name STRING -/// success_count INTEGER -/// failure_count INTEGER -/// is_flaky INTEGER -/// -/// This returns latest [LIMIT] number of build stats for each builder. -const String getBuilderStatisticQuery = r''' -select builder_name, - sum(is_flaky) as flaky_number, - count(*) as total_number, - string_agg(case when is_flaky = 1 then flaky_builds end, ', ') as flaky_builds, - string_agg(succeeded_builds, ', ') as succeeded_builds, - array_agg(case when is_flaky = 1 then sha end IGNORE NULLS ORDER BY date DESC)[ordinal(1)] as recent_flaky_commit, - array_agg(case when is_flaky = 1 then flaky_builds end IGNORE NULLS ORDER BY date DESC)[ordinal(1)] as flaky_build_of_recent_flaky_commit, - sum(is_flaky)/count(*) as flaky_ratio, - min(date) as from_date, - max(date) as to_date -from (select *, row_number() over (partition by builder_name order by time desc) as rank from `flutter-dashboard.datasite.luci_prod_build_status`) -where date>=date_sub(current_date(), interval 30 day) and - builder_name not like '%Drone' and - repo='flutter' and - branch='master' and - pool = 'luci.flutter.prod' and - builder_name not like '%Beta%' and - builder_name not like '% beta %' and - builder_name not like '%Stable%' and - builder_name not like '% stable %' and - builder_name not like '%Dev%' and - builder_name not like '% dev %' and - rank<=@LIMIT -group by builder_name; -'''; - -const String getStagingBuilderStatisticQuery = r''' -select builder_name, - sum(is_flaky) as flaky_number, - count(*) as total_number, - string_agg(case when is_flaky = 1 then flaky_builds end, ', ') as flaky_builds, - string_agg(succeeded_builds, ', ') as succeeded_builds, - array_agg(case when is_flaky = 1 then sha end IGNORE NULLS ORDER BY date DESC)[ordinal(1)] as recent_flaky_commit, - array_agg(case when is_flaky = 1 then flaky_builds end IGNORE NULLS ORDER BY date DESC)[ordinal(1)] as flaky_build_of_recent_flaky_commit, - sum(is_flaky)/count(*) as flaky_ratio, - min(date) as from_date, - max(date) as to_date -from (select *, row_number() over (partition by builder_name order by time desc) as rank from `flutter-dashboard.datasite.luci_staging_build_status`) -where date>=date_sub(current_date(), interval 30 day) and - builder_name not like '%Drone' and - repo='flutter' and - branch='master' and - pool = 'luci.flutter.staging' and - builder_name not like '%Beta%' and - builder_name not like '% beta %' and - rank<=@LIMIT -group by builder_name; -'''; - -// Returns builds in the past 30 days to exclude obsolete historical data. -const String getRecordsQuery = r''' -select sha, is_flaky, failure_count from `flutter-dashboard.datasite.luci_staging_build_status` -where builder_name=@BUILDER_NAME and date>=date_sub(current_date(), interval 30 day) -order by time desc -limit @LIMIT -'''; - -class BigqueryService { - const BigqueryService(this.accessClientProvider); - - /// AccessClientProvider for OAuth 2.0 authenticated access client - final AccessClientProvider accessClientProvider; - - /// Return a [TabledataResource] with an authenticated [client] - Future defaultTabledata() async { - final Client client = await accessClientProvider.createAccessClient( - scopes: const [BigqueryApi.bigqueryScope], - ); - return BigqueryApi(client).tabledata; - } - - /// Return a [JobsResource] with an authenticated [client] - Future defaultJobs() async { - final Client client = await accessClientProvider.createAccessClient( - scopes: const [BigqueryApi.bigqueryScope], - ); - return BigqueryApi(client).jobs; - } - - /// Return the top [limit] number of current builder statistic. - /// - /// See getBuilderStatisticQuery to get the detail information about the table - /// schema - Future> listBuilderStatistic( - String projectId, { - int limit = 100, - String bucket = 'prod', - }) async { - final JobsResource jobsResource = await defaultJobs(); - final QueryRequest query = QueryRequest.fromJson({ - 'query': bucket == 'staging' ? getStagingBuilderStatisticQuery : getBuilderStatisticQuery, - 'queryParameters': >[ - { - 'name': 'LIMIT', - 'parameterType': {'type': 'INT64'}, - 'parameterValue': {'value': '$limit'}, - }, - ], - 'useLegacySql': false, - }); - final QueryResponse response = await jobsResource.query(query, projectId); - if (!response.jobComplete!) { - throw 'job does not complete'; - } - final List result = []; - for (final TableRow row in response.rows!) { - final String builder = row.f![0].v as String; - List? flakyBuilds = (row.f![3].v as String?)?.split(', '); - flakyBuilds?.sort(); - flakyBuilds = flakyBuilds?.reversed.toList(); - List? succeededBuilds = (row.f![4].v as String?)?.split(', '); - succeededBuilds?.sort(); - succeededBuilds = succeededBuilds?.reversed.toList(); - result.add( - BuilderStatistic( - name: builder, - flakyRate: double.parse(row.f![7].v as String), - flakyBuilds: flakyBuilds ?? const [], - succeededBuilds: succeededBuilds ?? const [], - recentCommit: row.f![5].v as String?, - flakyBuildOfRecentCommit: row.f![6].v as String?, - flakyNumber: int.parse(row.f![1].v as String), - totalNumber: int.parse(row.f![2].v as String), - fromDate: row.f![8].v as String?, - toDate: row.f![9].v as String?, - ), - ); - } - return result; - } - - /// Return the list of current builder statistic. - /// - /// See getBuilderStatisticQuery to get the detail information about the table - /// schema - Future> listRecentBuildRecordsForBuilder( - String projectId, { - String? builder, - int? limit, - }) async { - final JobsResource jobsResource = await defaultJobs(); - final QueryRequest query = QueryRequest.fromJson({ - 'query': getRecordsQuery, - 'parameterMode': 'NAMED', - 'queryParameters': >[ - { - 'name': 'BUILDER_NAME', - 'parameterType': {'type': 'STRING'}, - 'parameterValue': {'value': builder}, - }, - { - 'name': 'LIMIT', - 'parameterType': {'type': 'INT64'}, - 'parameterValue': {'value': '$limit'}, - }, - ], - 'useLegacySql': false, - }); - final QueryResponse response = await jobsResource.query(query, projectId); - if (!response.jobComplete!) { - throw 'job does not complete'; - } - final List result = []; - // When a test is newly marked as flaky, it is possible no execution exists. - if (response.rows == null) { - return result; - } - for (final TableRow row in response.rows!) { - result.add( - BuilderRecord( - commit: row.f![0].v as String, - isFlaky: row.f![1].v as String != '0', - isFailed: row.f![2].v as String != '0', - ), - ); - } - return result; - } -} - -class BuilderRecord { - BuilderRecord({ - required this.commit, - required this.isFlaky, - required this.isFailed, - }); - - final String commit; - final bool isFlaky; - final bool isFailed; -} - -class BuilderStatistic { - BuilderStatistic({ - required this.name, - required this.flakyRate, - required this.flakyNumber, - required this.totalNumber, - this.flakyBuilds, - this.succeededBuilds, - this.recentCommit, - this.flakyBuildOfRecentCommit, - this.fromDate, - this.toDate, - }); - - final String name; - final double flakyRate; - final List? flakyBuilds; - final List? succeededBuilds; - final String? recentCommit; - final String? flakyBuildOfRecentCommit; - final int flakyNumber; - final int totalNumber; - final String? fromDate; - final String? toDate; -} diff --git a/app_dart/lib/src/service/branch_service.dart b/app_dart/lib/src/service/branch_service.dart deleted file mode 100644 index a6072d914..000000000 --- a/app_dart/lib/src/service/branch_service.dart +++ /dev/null @@ -1,171 +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/service/config.dart'; -import 'package:cocoon_service/src/service/github_service.dart'; -import 'package:github/github.dart' as gh; -import 'package:retry/retry.dart'; - -import '../model/gerrit/commit.dart'; -import '../request_handling/exceptions.dart'; -import 'gerrit_service.dart'; -import 'logging.dart'; - -class RetryException implements Exception {} - -/// A class to manage GitHub branches. -/// -/// Track branch activities such as branch creation, and helps manage release branches. -class BranchService { - BranchService({ - required this.config, - required this.gerritService, - this.retryOptions = const RetryOptions(maxAttempts: 3), - }); - - final Config config; - final GerritService gerritService; - final RetryOptions retryOptions; - - /// Creates a flutter/recipes branch that aligns to a flutter/engine commit. - /// - /// Take the example repo history: - /// flutter/engine: A -> B -> C -> D -> E - /// flutter/recipes: V -> W -> X -> Y -> Z - /// - /// If flutter/engine branches at C, this finds the flutter/recipes commit that should be used for C. - /// The best guess for a flutter/recipes commit that aligns with C is whatever was the most recently committed - /// before C was committed. - /// - /// Once the flutter/recipes commit is found, it is branched to match flutter/engine. - /// - /// Generally, this should work. However, some edge cases may require CPs. Such as when commits land in a - /// short timespan, and require the release manager to CP onto the recipes branch (in the case of reverts). - Future branchFlutterRecipes(String branch, String engineSha) async { - final gh.RepositorySlug recipesSlug = gh.RepositorySlug('flutter', 'recipes'); - if ((await gerritService.branches( - '${recipesSlug.owner}-review.googlesource.com', - recipesSlug.name, - filterRegex: branch, - )) - .contains(branch)) { - // subString is a regex, and can return multiple matches - log.warning('$branch already exists for $recipesSlug'); - throw BadRequestException('$branch already exists'); - } - final Iterable recipeCommits = - await gerritService.commits(recipesSlug, Config.defaultBranch(recipesSlug)); - log.info('$recipesSlug commits: $recipeCommits'); - final gh.RepositoryCommit engineCommit = await retryOptions.retry( - () async { - final GithubService githubService = await config.createDefaultGitHubService(); - return githubService.github.repositories.getCommit(Config.engineSlug, engineSha); - }, - retryIf: (Exception e) => e is gh.GitHubError, - ); - log.info('${Config.engineSlug} commit: $engineCommit'); - final DateTime? branchTime = engineCommit.commit?.committer?.date; - if (branchTime == null) { - throw BadRequestException('$engineSha has no commit time'); - } - log.info('Searching for a recipe commit before $branchTime'); - for (GerritCommit recipeCommit in recipeCommits) { - final DateTime? recipeTime = recipeCommit.author?.time; - - if (recipeTime != null && recipeTime.isBefore(branchTime)) { - final String revision = recipeCommit.commit!; - return gerritService.createBranch(recipesSlug, branch, revision); - } - } - - throw InternalServerError('Failed to find a revision to flutter/recipes for $branch before $branchTime'); - } - - /// Returns a Map that contains the latest google3 roll, beta, and stable branches. - /// - /// Latest beta and stable branches are retrieved based on 'beta' and 'stable' tags. Dev branch is retrived - /// as the latest flutter candidate branch. - Future>> getReleaseBranches({ - required GithubService githubService, - required gh.RepositorySlug slug, - }) async { - final List branches = await githubService.github.repositories.listBranches(slug).toList(); - final String latestCandidateBranch = await _getLatestCandidateBranch( - github: githubService.github, - slug: slug, - branches: branches, - ); - - final String betaName = await _getBranchNameFromFile( - githubService: githubService, - slug: slug, - branchName: 'beta', - ); - final String stableName = await _getBranchNameFromFile( - githubService: githubService, - slug: slug, - branchName: 'stable', - ); - return >[ - { - 'branch': Config.defaultBranch(slug), - 'name': 'HEAD', - }, - { - 'branch': stableName, - 'name': 'stable', - }, - { - 'branch': betaName, - 'name': 'beta', - }, - { - 'branch': latestCandidateBranch, - 'name': 'dev', - }, - ]; - } - - Future _getBranchNameFromFile({ - required GithubService githubService, - required gh.RepositorySlug slug, - required String branchName, - }) async { - return (await githubService.getFileContent( - slug, - 'bin/internal/release-candidate-branch.version', - ref: branchName, - )) - .trim(); - } - - /// Retrieve the latest canidate branch from all candidate branches. - Future _getLatestCandidateBranch({ - required gh.GitHub github, - required gh.RepositorySlug slug, - required List branches, - }) async { - final RegExp candidateBranchName = RegExp(r'^flutter-\d+\.\d+-candidate\.\d+$'); - final List devBranches = branches.where((gh.Branch b) => candidateBranchName.hasMatch(b.name!)).toList(); - devBranches.sort((b, a) => (_versionSum(a.name!)).compareTo(_versionSum(b.name!))); - final String devBranchName = devBranches.take(1).single.name!; - return devBranchName; - } - - /// Helper function to convert candidate branch versions to numbers for comparison. - int _versionSum(String tagOrBranchName) { - final List digits = tagOrBranchName.replaceAll(r'flutter|candidate', '0').split(RegExp(r'\.|\-')); - int versionSum = 0; - for (String digit in digits) { - final int? d = int.tryParse(digit); - if (d == null) { - continue; - } - versionSum = versionSum * 100 + d; - } - return versionSum; - } -} diff --git a/app_dart/lib/src/service/build_status_provider.dart b/app_dart/lib/src/service/build_status_provider.dart deleted file mode 100644 index 8f1fc63b0..000000000 --- a/app_dart/lib/src/service/build_status_provider.dart +++ /dev/null @@ -1,222 +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 '../model/appengine/commit.dart'; -import '../model/appengine/github_build_status_update.dart'; -import '../model/appengine/stage.dart'; -import '../model/appengine/task.dart'; -import 'datastore.dart'; - -/// Function signature for a [BuildStatusService] provider. -typedef BuildStatusServiceProvider = BuildStatusService Function(DatastoreService datastoreService); - -/// Branches that are used to calculate the tree status. -const Set defaultBranches = {'refs/heads/main', 'refs/heads/master'}; - -/// Class that calculates the current build status. -class BuildStatusService { - const BuildStatusService(this.datastoreService); - - final DatastoreService datastoreService; - - /// Creates and returns a [DatastoreService] using [db] and [maxEntityGroups]. - static BuildStatusService defaultProvider(DatastoreService datastoreService) { - return BuildStatusService(datastoreService); - } - - @visibleForTesting - static const int numberOfCommitsToReferenceForTreeStatus = 20; - - /// Calculates and returns the "overall" status of the Flutter build. - /// - /// This calculation operates by looking for the most recent success or - /// failure for every (non-flaky) task in the manifest. - /// - /// Take the example build dashboard below: - /// ✔ = passed, ✖ = failed, ☐ = new, ░ = in progress, s = skipped - /// +---+---+---+---+ - /// | A | B | C | D | - /// +---+---+---+---+ - /// | ✔ | ☐ | ░ | s | - /// +---+---+---+---+ - /// | ✔ | ░ | ✔ | ✖ | - /// +---+---+---+---+ - /// | ✔ | ✖ | ✔ | ✔ | - /// +---+---+---+---+ - /// This build will fail because of Task B only. Task D is not included in - /// the latest commit status, so it does not impact the build status. - /// Task B fails because its last known status was to be failing, even though - /// there is currently a newer version that is in progress. - /// - /// Tree status is only for [defaultBranches]. - Future calculateCumulativeStatus(RepositorySlug slug) async { - final List statuses = await retrieveCommitStatus( - limit: numberOfCommitsToReferenceForTreeStatus, - slug: slug, - ).toList(); - if (statuses.isEmpty) { - return BuildStatus.failure(); - } - - final Map tasksInProgress = _findTasksRelevantToLatestStatus(statuses); - if (tasksInProgress.isEmpty) { - return BuildStatus.failure(); - } - - final List failedTasks = []; - for (CommitStatus status in statuses) { - for (Stage stage in status.stages) { - for (Task task in stage.tasks) { - /// If a task [isRelevantToLatestStatus] but has not run yet, we look - /// for a previous run of the task from the previous commit. - final bool isRelevantToLatestStatus = tasksInProgress.containsKey(task.name); - - /// Tasks that are not relevant to the latest status will have a - /// null value in the map. - final bool taskInProgress = tasksInProgress[task.name] ?? true; - - if (isRelevantToLatestStatus && taskInProgress) { - if (task.isFlaky! || _isSuccessful(task)) { - /// This task no longer needs to be checked to see if it causing - /// the build status to fail. - tasksInProgress[task.name!] = false; - } else if (_isFailed(task) || _isRerunning(task)) { - failedTasks.add(task.name!); - - /// This task no longer needs to be checked to see if its causing - /// the build status to fail since its been - /// added to the failedTasks list. - tasksInProgress[task.name!] = false; - } - } - } - } - } - return failedTasks.isNotEmpty ? BuildStatus.failure(failedTasks) : BuildStatus.success(); - } - - /// Creates a map of the tasks that need to be checked for the build status. - /// - /// This is based on the most recent [CommitStatus] and all of its tasks. - Map _findTasksRelevantToLatestStatus(List statuses) { - final Map tasks = {}; - - for (Stage stage in statuses.first.stages) { - for (Task task in stage.tasks) { - tasks[task.name!] = true; - } - } - - return tasks; - } - - /// Retrieves the comprehensive status of every task that runs per commit. - /// - /// The returned stream will be ordered by most recent commit first, then - /// the next newest, and so on. - Stream retrieveCommitStatus({ - required int limit, - int? timestamp, - String? branch, - required RepositorySlug slug, - }) async* { - await for (Commit commit in datastoreService.queryRecentCommits( - limit: limit, - timestamp: timestamp, - branch: branch, - slug: slug, - )) { - final List stages = await datastoreService.queryTasksGroupedByStage(commit); - yield CommitStatus(commit, stages); - } - } - - bool _isFailed(Task task) { - return task.status == Task.statusFailed || - task.status == Task.statusInfraFailure || - task.status == Task.statusCancelled; - } - - bool _isSuccessful(Task task) { - return task.status == Task.statusSucceeded; - } - - bool _isRerunning(Task task) { - return task.attempts! > 1 && (task.status == Task.statusInProgress || task.status == Task.statusNew); - } -} - -/// Class that holds the status for all tasks corresponding to a particular -/// commit. -/// -/// Tasks may still be running, and thus their status is subject to change. -/// Put another way, this class holds information that is a snapshot in time. -@immutable -class CommitStatus { - /// Creates a new [CommitStatus]. - const CommitStatus(this.commit, this.stages); - - /// The commit against which all the tasks in [stages] are run. - final Commit commit; - - /// The partitioned stages, each of which holds a bucket of tasks that - /// belong in the stage. - final List stages; -} - -@immutable -class BuildStatus { - const BuildStatus._(this.value, [this.failedTasks = const []]) - : assert(value == GithubBuildStatusUpdate.statusSuccess || value == GithubBuildStatusUpdate.statusFailure); - factory BuildStatus.success() => const BuildStatus._(GithubBuildStatusUpdate.statusSuccess); - factory BuildStatus.failure([List failedTasks = const []]) => - BuildStatus._(GithubBuildStatusUpdate.statusFailure, failedTasks); - - final String value; - final List failedTasks; - - bool get succeeded { - return value == GithubBuildStatusUpdate.statusSuccess; - } - - String get githubStatus => value; - - @override - int get hashCode { - int hash = 17; - hash = hash * 31 + value.hashCode; - hash = hash * 31 + failedTasks.hashCode; - return hash; - } - - @override - bool operator ==(Object other) { - if (identical(this, other)) { - return true; - } - if (other is BuildStatus) { - if (value != other.value) { - return false; - } - if (other.failedTasks.length != failedTasks.length) { - return false; - } - for (int i = 0; i < failedTasks.length; ++i) { - if (failedTasks[i] != other.failedTasks[i]) { - return false; - } - } - return true; - } - return false; - } - - @override - String toString() => '$value $failedTasks'; -} diff --git a/app_dart/lib/src/service/buildbucket.dart b/app_dart/lib/src/service/buildbucket.dart deleted file mode 100644 index 873dab269..000000000 --- a/app_dart/lib/src/service/buildbucket.dart +++ /dev/null @@ -1,210 +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:io'; - -import 'package:googleapis_auth/auth_io.dart'; -import 'package:http/http.dart' as http; -import 'package:meta/meta.dart'; - -import '../model/luci/buildbucket.dart'; -import '../request_handling/body.dart'; -import 'access_token_provider.dart'; - -import '../service/logging.dart'; - -/// A client interface to LUCI BuildBucket -@immutable -class BuildBucketClient { - /// Creates a new build bucket Client. - /// - /// The [buildBucketUri] parameter must not be null, and will be defaulted to - /// [kDefaultBuildBucketUri] if not specified. - /// - /// The [httpClient] parameter will be defaulted to `HttpClient()` if not - /// specified or null. - BuildBucketClient({ - this.buildBucketBuilderUri = kDefaultBuildBucketBuilderUri, - this.buildBucketBuildUri = kDefaultBuildBucketBuildUri, - this.accessTokenService, - http.Client? httpClient, - }) : httpClient = httpClient ?? http.Client(); - - /// Garbage to prevent browser/JSON parsing exploits. - static const String kRpcResponseGarbage = ")]}'"; - - /// The default endpoint for BuildBucket build requests. - static const String kDefaultBuildBucketBuildUri = 'https://cr-buildbucket.appspot.com/prpc/buildbucket.v2.Builds'; - - /// The default endpoint for BuildBucket builder requests. - static const String kDefaultBuildBucketBuilderUri = 'https://cr-buildbucket.appspot.com/prpc/buildbucket.v2.Builders'; - - /// The base URI for build bucket requests. - /// - /// Defaults to [kDefaultBuildBucketBuildUri]. - final String buildBucketBuildUri; - - /// The base URI for build bucket requests. - /// - /// Defaults to [kDefaultBuildBucketBuilderUri]. - final String buildBucketBuilderUri; - - /// The token provider for OAuth2 requests. - /// - /// If this is non-null, an access token will be attached to any outbound - /// HTTP requests issued by this client. - final AccessTokenService? accessTokenService; - - /// The [http.Client] to use for requests. - final http.Client httpClient; - - Future _postRequest( - String path, - S request, - T Function(Map? rawResponse) responseFromJson, { - String buildBucketUri = kDefaultBuildBucketBuildUri, - }) async { - final Uri url = Uri.parse('$buildBucketUri$path'); - final AccessToken? token = await accessTokenService?.createAccessToken(); - - log.fine('Making request with path: $url and body: ${json.encode(request)}'); - - final http.Response response = await httpClient.post( - url, - body: json.encode(request), - headers: { - HttpHeaders.contentTypeHeader: 'application/json', - HttpHeaders.acceptHeader: 'application/json', - if (token != null) HttpHeaders.authorizationHeader: '${token.type} ${token.data}', - }, - ); - - if (response.statusCode < 300) { - return responseFromJson( - json.decode(response.body.substring(kRpcResponseGarbage.length)) as Map?, - ); - } - throw BuildBucketException(response.statusCode, response.body); - } - - /// The RPC request to schedule a build. - Future scheduleBuild( - ScheduleBuildRequest request, { - String buildBucketUri = kDefaultBuildBucketBuildUri, - }) { - return _postRequest( - '/ScheduleBuild', - request, - Build.fromJson, - buildBucketUri: buildBucketUri, - ); - } - - /// The RPC request to search for builds. - Future searchBuilds( - SearchBuildsRequest request, { - String buildBucketUri = kDefaultBuildBucketBuildUri, - }) { - return _postRequest( - '/SearchBuilds', - request, - SearchBuildsResponse.fromJson, - buildBucketUri: buildBucketUri, - ); - } - - /// The RPC method to batch multiple RPC methods in a single HTTP request. - /// - /// The response is guaranteed to contain line-item responses for all - /// line-item requests that were issued in [request]. If only a subset of - /// responses were retrieved, a [BatchRequestException] will be thrown. - Future batch( - BatchRequest request, { - String buildBucketUri = kDefaultBuildBucketBuildUri, - }) async { - final BatchResponse response = await _postRequest( - '/Batch', - request, - BatchResponse.fromJson, - buildBucketUri: buildBucketUri, - ); - if (response.responses!.length != request.requests!.length) { - throw BatchRequestException('Failed to execute all requests'); - } - return response; - } - - /// The RPC request to cancel a build. - Future cancelBuild( - CancelBuildRequest request, { - String buildBucketUri = kDefaultBuildBucketBuildUri, - }) { - return _postRequest( - '/CancelBuild', - request, - Build.fromJson, - buildBucketUri: buildBucketUri, - ); - } - - /// The RPC request to get details about a build. - Future getBuild( - GetBuildRequest request, { - String buildBucketUri = kDefaultBuildBucketBuildUri, - }) { - return _postRequest( - '/GetBuild', - request, - Build.fromJson, - buildBucketUri: buildBucketUri, - ); - } - - /// The RPC request to get a list of builders. - Future listBuilders( - ListBuildersRequest request, { - String buildBucketUri = kDefaultBuildBucketBuilderUri, - }) { - return _postRequest( - '/ListBuilders', - request, - ListBuildersResponse.fromJson, - buildBucketUri: buildBucketUri, - ); - } - - /// Closes the underlying [HttpClient]. - /// - /// If `force` is true, it will close immediately and cause outstanding - /// requests to end with an error. Otherwise, it will wait for outstanding - /// requests to finish before closing. - /// - /// Once this call completes, additional RPC requests will throw an exception. - void close() { - httpClient.close(); - } -} - -class BuildBucketException implements Exception { - const BuildBucketException(this.statusCode, this.message); - - /// The HTTP status code of the error. - final int statusCode; - - /// The message from the server. - final String message; - - @override - String toString() => '$runtimeType: [$statusCode]: $message'; -} - -class BatchRequestException implements Exception { - BatchRequestException(this.message); - - final String message; - - @override - String toString() => message; -} diff --git a/app_dart/lib/src/service/cache_service.dart b/app_dart/lib/src/service/cache_service.dart deleted file mode 100644 index ba172fbbb..000000000 --- a/app_dart/lib/src/service/cache_service.dart +++ /dev/null @@ -1,217 +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:meta/meta.dart'; -import 'package:neat_cache/cache_provider.dart'; -import 'package:neat_cache/neat_cache.dart'; -import 'package:mutex/mutex.dart'; -import 'package:retry/retry.dart'; - -import 'logging.dart'; - -/// Service for reading and writing values to a cache for quick access of data. -/// -/// If [inMemory] is true, a cache with [inMemoryMaxNumberEntries] number -/// of entries will be created. Otherwise, it will use the default redis cache. -class CacheService { - CacheService({ - bool inMemory = false, - int inMemoryMaxNumberEntries = 256, - }) : _provider = - inMemory ? Cache.inMemoryCacheProvider(inMemoryMaxNumberEntries) : Cache.redisCacheProvider(memorystoreUri); - - final Mutex m = Mutex(); - - final CacheProvider> _provider; - - Cache get cache => cacheValue ?? Cache>(_provider).withCodec(const _CacheCodec()); - - @visibleForTesting - Cache? cacheValue; - - /// Google Cloud Memorystore default url. - static Uri memorystoreUri = Uri.parse('redis://10.0.0.4:6379'); - - /// An arbritary number for how many times we should try to get from cache - /// before giving up. - /// - /// Writing to the cache creates a racy condition for when another operation - /// is trying to get the same key. This race condition throws an exception. - @visibleForTesting - static const int maxCacheGetAttempts = 3; - - /// Get value of [key] from the subcache [subcacheName]. If the key has no - /// value, call [createFn] to create a value for it, set it, and return it. - /// - /// The underlying cache get function is inherently racy as if there is a - /// write operation while a read operation, getting the value can fail. To - /// handle this racy condition, this attempts to get the value [maxCacheGetAttempts] - /// times before giving up. This is because the cache is magnitudes faster - /// than the fallback operation (usually a Datastore query). - Future getOrCreate( - String subcacheName, - String key, { - required Future Function()? createFn, - Duration ttl = const Duration(minutes: 1), - }) async { - Uint8List? value = await _readValue(subcacheName, key); - - // If given createFn, update the cache value if the value returned was null. - if (createFn != null && value == null) { - // Try creating the value - value = await createFn(); - await set( - subcacheName, - key, - value, - ttl: ttl, - ); - } - - return value; - } - - /// This method is the same as the [getOrCreate] method above except that it - /// enforces locking access. - /// - /// Note: these methods are intended to prevent issues around race conditions - /// when storing and retrieving github tokens locally only for this instance. - /// Care should be taken to use the locking methods together when accessing - /// data from an entity using the cache. - Future getOrCreateWithLocking( - String subcacheName, - String key, { - required Future Function()? createFn, - Duration ttl = const Duration(minutes: 1), - }) async { - Uint8List? value = await _readValue(subcacheName, key); - - // If given createFn, update the cache value if the value returned was null. - if (createFn != null && value == null) { - // Try creating the value - value = await createFn(); - await setWithLocking( - subcacheName, - key, - value, - ttl: ttl, - ); - } - - return value; - } - - Future _readValue( - String subcacheName, - String key, - ) async { - final Cache subcache = cache.withPrefix(subcacheName); - Uint8List? value; - - const RetryOptions r = RetryOptions( - maxAttempts: maxCacheGetAttempts, - delayFactor: Duration(milliseconds: 50), - ); - - try { - await r.retry( - () async { - value = await subcache[key].get(); - }, - ); - } catch (e) { - // If the last retry is unsuccessful on an exception we do not want to die - // here. - log.warning('Unable to retrieve value for $key from cache.'); - value = null; - } - - return value; - } - - /// Set [value] for [key] in the subcache [subcacheName] with [ttl]. - Future set( - String subcacheName, - String key, - Uint8List? value, { - Duration ttl = const Duration(minutes: 1), - }) async { - final Cache subcache = cache.withPrefix(subcacheName); - final Entry entry = subcache[key]; - return entry.set(value, ttl); - } - - /// Set [value] for [key] in the subcache [subcacheName] with [ttl] but - /// enforce locking accessing. - /// - /// Note: these methods are intended to prevent issues around race conditions - /// when storing and retrieving github tokens. Care should be taken to use the - /// locking methods together when accessing data from an entity using the - /// cache. - Future setWithLocking( - String subcacheName, - String key, - Uint8List? value, { - Duration ttl = const Duration(minutes: 1), - }) async { - await m.acquire(); - try { - return set( - subcacheName, - key, - value, - ttl: ttl, - ); - } finally { - m.release(); - } - } - - /// Clear the value stored in subcache [subcacheName] for key [key]. - /// - /// Note: these methods are intended to prevent issues around race conditions - /// when storing and retrieving github tokens. Care should be taken to use the - /// locking methods together when accessing data from an entity using the - /// cache. - Future purge(String subcacheName, String key) async { - await m.acquire(); - try { - final Cache subcache = cache.withPrefix(subcacheName); - return subcache[key].purge(retries: maxCacheGetAttempts); - } finally { - m.release(); - } - } - - void dispose() { - _provider.close(); - } -} - -class _CacheCodec extends Codec> { - const _CacheCodec(); - - @override - Converter> get encoder => const _ListIntConverter(); - - @override - Converter, Uint8List> get decoder => const _Uint8ListConverter(); -} - -class _ListIntConverter extends Converter> { - const _ListIntConverter(); - - @override - List convert(Uint8List input) => input; -} - -class _Uint8ListConverter extends Converter, Uint8List> { - const _Uint8ListConverter(); - - @override - Uint8List convert(List input) => Uint8List.fromList(input); -} diff --git a/app_dart/lib/src/service/commit_service.dart b/app_dart/lib/src/service/commit_service.dart deleted file mode 100644 index 61289dabb..000000000 --- a/app_dart/lib/src/service/commit_service.dart +++ /dev/null @@ -1,95 +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/service/config.dart'; -import 'package:cocoon_service/src/service/github_service.dart'; -import 'package:github/github.dart'; -import 'package:meta/meta.dart'; -import 'package:truncate/truncate.dart'; - -import 'logging.dart'; -import 'package:cocoon_service/src/service/datastore.dart'; -import 'package:gcloud/db.dart'; -import 'package:github/hooks.dart'; - -/// A class for doing various actions related to Github commits. -class CommitService { - CommitService({ - required this.config, - @visibleForTesting this.datastoreProvider = DatastoreService.defaultProvider, - }); - - final Config config; - final DatastoreServiceProvider datastoreProvider; - - /// Add a commit based on a [CreateEvent] to the Datastore. - Future handleCreateGithubRequest(CreateEvent createEvent) async { - final DatastoreService datastore = datastoreProvider(config.db); - final RepositorySlug slug = RepositorySlug.full(createEvent.repository!.fullName); - final String branch = createEvent.ref!; - log.info('Creating commit object for branch $branch in repository ${slug.fullName}'); - final Commit commit = await _createCommitFromBranchEvent(datastore, slug, branch); - await _insertCommitIntoDatastore(datastore, commit); - } - - /// Add a commit based on a Push event to the Datastore. - /// https://docs.github.com/en/webhooks/webhook-events-and-payloads#push - Future handlePushGithubRequest(Map pushEvent) async { - final DatastoreService datastore = datastoreProvider(config.db); - final RepositorySlug slug = RepositorySlug.full(pushEvent['repository']['full_name']); - final String sha = pushEvent['head_commit']['id']; - final String branch = pushEvent['ref'].split('/')[2]; - final String id = '${slug.fullName}/$branch/$sha'; - final Key key = datastore.db.emptyKey.append(Commit, id: id); - final Commit commit = Commit( - key: key, - timestamp: DateTime.parse(pushEvent['head_commit']['timestamp']).millisecondsSinceEpoch, - repository: slug.fullName, - sha: sha, - author: pushEvent['sender']['login'], - authorAvatarUrl: pushEvent['sender']['avatar_url'], - // The field has a size of 1500 we need to ensure the commit message - // is at most 1500 chars long. - message: truncate(pushEvent['head_commit']['message'], 1490, omission: '...'), - branch: branch, - ); - await _insertCommitIntoDatastore(datastore, commit); - } - - Future _createCommitFromBranchEvent(DatastoreService datastore, RepositorySlug slug, String branch) async { - final GithubService githubService = await config.createDefaultGitHubService(); - final GitReference gitRef = await githubService.getReference(slug, 'heads/$branch'); - final String sha = gitRef.object!.sha!; - final RepositoryCommit commit = await githubService.github.repositories.getCommit(slug, sha); - - final String id = '${slug.fullName}/$branch/$sha'; - final Key key = datastore.db.emptyKey.append(Commit, id: id); - return Commit( - key: key, - timestamp: DateTime.now().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, - ); - } - - Future _insertCommitIntoDatastore(DatastoreService datastore, Commit commit) async { - final DatastoreService datastore = datastoreProvider(config.db); - try { - log.info('Checking for existing commit in the datastore'); - await datastore.lookupByValue(commit.key); - } on KeyNotFoundException { - log.info('Commit does not exist in datastore, inserting into datastore'); - await datastore.insert([commit]); - } - } -} diff --git a/app_dart/lib/src/service/config.dart b/app_dart/lib/src/service/config.dart deleted file mode 100644 index f1574435d..000000000 --- a/app_dart/lib/src/service/config.dart +++ /dev/null @@ -1,443 +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:appengine/appengine.dart'; -import 'package:cocoon_service/src/service/datastore.dart'; -import 'package:corsac_jwt/corsac_jwt.dart'; -import 'package:gcloud/db.dart'; -import 'package:gcloud/service_scope.dart' as ss; -import 'package:github/github.dart' as gh; -import 'package:googleapis/bigquery/v2.dart'; -import 'package:graphql/client.dart'; -import 'package:http/http.dart' as http; -import 'package:meta/meta.dart'; -import 'package:retry/retry.dart'; - -import '../../cocoon_service.dart'; -import '../model/appengine/branch.dart'; -import '../model/appengine/cocoon_config.dart'; -import '../model/appengine/key_helper.dart'; -import 'access_client_provider.dart'; -import 'bigquery.dart'; -import 'github_service.dart'; -import 'logging.dart'; - -/// Name of the default git branch. -const String kDefaultBranchName = 'master'; - -class Config { - Config(this._db, this._cache); - - final DatastoreDB _db; - - final CacheService _cache; - - /// List of Github presubmit supported repos. - /// - /// This adds support for the `waiting for tree to go green label` to the repo. - /// - /// Relies on the GitHub Checks API being enabled for this repo. - Set get supportedRepos => { - cocoonSlug, - engineSlug, - flutterSlug, - packagesSlug, - }; - - /// List of guaranteed scheduling Github repos. - static Set get guaranteedSchedulingRepos => { - engineSlug, - packagesSlug, - }; - - /// List of Github postsubmit supported repos. - /// - /// This adds support for check runs to the repo. - Set get postsubmitSupportedRepos => { - packagesSlug, - }; - - /// GitHub repositories that use CI status to determine if pull requests can be submitted. - static Set reposWithTreeStatus = { - engineSlug, - flutterSlug, - }; - - /// The tip of tree branch for [slug]. - static String defaultBranch(gh.RepositorySlug slug) { - final Map defaultBranches = { - cocoonSlug: 'main', - flutterSlug: 'master', - engineSlug: 'main', - packagesSlug: 'main', - recipesSlug: 'main', - }; - - return defaultBranches[slug] ?? kDefaultBranchName; - } - - // The name of the bot that generates automated revert requests. - String get autosubmitBot => 'auto-submit[bot]'; - - // The name of the label that the bot uses to identify the automatically - // created pull request. - static const String revertOfLabel = 'revert of'; - - /// Memorystore subcache name to store [CocoonConfig] values in. - static const String configCacheName = 'config'; - - /// Default properties when rerunning a prod build. - static const Map defaultProperties = {'force_upload': true}; - - @visibleForTesting - static const Duration configCacheTtl = Duration(hours: 12); - - Logging get loggingService => ss.lookup(#appengine.logging) as Logging; - - Future> getBranches(gh.RepositorySlug slug) async { - final DatastoreService datastore = DatastoreService(db, defaultMaxEntityGroups); - final List branches = await (datastore.queryBranches().toList()); - - return branches.where((Branch branch) => branch.slug == slug); - } - - Future> _getReleaseAccounts() async { - final String releaseAccountsConcat = await _getSingleValue('ReleaseAccounts'); - return releaseAccountsConcat.split(','); - } - - Future _getSingleValue(String id) async { - final Uint8List? cacheValue = await _cache.getOrCreate( - configCacheName, - id, - createFn: () => _getValueFromDatastore(id), - ttl: configCacheTtl, - ); - - return String.fromCharCodes(cacheValue!); - } - - Future _getValueFromDatastore(String id) async { - final CocoonConfig cocoonConfig = CocoonConfig() - ..id = id - ..parentKey = _db.emptyKey; - final CocoonConfig result = await _db.lookupValue(cocoonConfig.key); - - return Uint8List.fromList(result.value.codeUnits); - } - - // GitHub App properties. - Future get githubPrivateKey => _getSingleValue('githubapp_private_pem'); - Future get overrideTreeStatusLabel => _getSingleValue('override_tree_status_label'); - Future get githubPublicKey => _getSingleValue('githubapp_public_pem'); - Future get githubAppId => _getSingleValue('githubapp_id'); - Future> get githubAppInstallations async { - final String installations = await _getSingleValue('githubapp_installations'); - return jsonDecode(installations) as Map; - } - - // Default recipe bundle used when the PR's base branch name does not exist in - // the recipes GoB project. - String get defaultRecipeBundleRef => 'refs/heads/main'; - - DatastoreDB get db => _db; - - /// Size of the shards to send to buildBucket when scheduling builds. - int get schedulingShardSize => 5; - - /// Batch size of builds to schedule in each swarming request. - int get batchSize => 5; - - /// Upper limit of targets to be backfilled in API call. - /// - /// For example, if we have 200 available targets found to be backfilled, - /// only `backfillerTargetLimit` will be scheduled whereas others wait for - /// the next API call. - int get backfillerTargetLimit => 50; - - /// Upper limit of commit rows to be backfilled in API call. - /// - /// This limits the number of commits to be checked to backfill. When bots - /// are idle, we hope to scan as many commit rows as possible. - int get backfillerCommitLimit => 50; - - /// Upper limit of issue/PRs allowed each API call. - /// - /// GitHub enforces a secondary rate limit on frequency API calls. This causes - /// our API failure when many issues/PRs are created in a short time. - int get issueAndPRLimit => 2; - - /// Max retries when scheduling builds. - static const RetryOptions schedulerRetry = RetryOptions(maxAttempts: 3); - - /// List of GitHub accounts related to releases. - Future> get releaseAccounts => _getReleaseAccounts(); - - Future get oauthClientId => _getSingleValue('OAuthClientId'); - - /// Webhook secret for the "Flutter Roll on Borg" GitHub App. - Future get frobWebhookKey => _getSingleValue('FrobWebhookKey'); - - Future get githubOAuthToken => _getSingleValue('GitHubPRToken'); - - String get wrongBaseBranchPullRequestMessage => 'This pull request was opened against a branch other than ' - '_{{default_branch}}_. Since Flutter pull requests should not ' - 'normally be opened against branches other than {{default_branch}}, I ' - 'have changed the base to {{default_branch}}. If this was intended, you ' - 'may modify the base back to {{target_branch}}. See the [Release Process]' - '(https://github.com/flutter/flutter/wiki/Release-process) for information ' - 'about how other branches get updated.\n\n' - '__Reviewers__: Use caution before merging pull requests to branches other ' - 'than {{default_branch}}, unless this is an intentional hotfix/cherrypick.'; - - String wrongHeadBranchPullRequestMessage(String branch) => - 'This pull request is trying merge the branch $branch, which is the name ' - 'of a release branch. This is usually a mistake. See ' - '[Tree Hygiene](https://github.com/flutter/flutter/wiki/Tree-hygiene) ' - 'for detailed instructions on how to contribute to the Flutter project. ' - 'In particular, ensure that before you start coding, you create your ' - 'feature branch off of _${kDefaultBranchName}_.\n\n' - 'This PR has been closed. If you are sure you want to merge $branch, you ' - 'may re-open this issue.'; - - String get releaseBranchPullRequestMessage => 'This pull request was opened ' - 'from and to a release candidate branch. This should only be done as part ' - 'of the official [Flutter release process]' - '(https://github.com/flutter/flutter/wiki/Release-process). If you are ' - 'attempting to make a regular contribution to the Flutter project, please ' - 'close this PR and follow the instructions at [Tree Hygiene]' - '(https://github.com/flutter/flutter/wiki/Tree-hygiene) for detailed ' - 'instructions on contributing to Flutter.\n\n' - '__Reviewers__: Use caution before merging pull requests to release ' - 'branches. Ensure the proper procedure has been followed.'; - - Future get webhookKey => _getSingleValue('WebhookKey'); - - String get mergeConflictPullRequestMessage => 'This pull request is not ' - 'mergeable in its current state, likely because of a merge conflict. ' - 'Pre-submit CI jobs were not triggered. Pushing a new commit to this ' - 'branch that resolves the issue will result in pre-submit jobs being ' - 'scheduled.'; - - String get missingTestsPullRequestMessage => 'It looks like this pull ' - 'request may not have tests. Please make sure to add tests before merging. ' - 'If you need ' - '[an exemption](https://github.com/flutter/flutter/wiki/Tree-hygiene#tests) ' - 'to this rule, contact Hixie or stuartmorgan on the #hackers ' - 'channel in [Chat](https://github.com/flutter/flutter/wiki/Chat) ' - '(don\'t just cc them here, they won\'t see it! Use Discord!).' - '\n\n' - 'If you are not sure if you need tests, consider this rule of thumb: ' - 'the purpose of a test is to make sure someone doesn\'t accidentally ' - 'revert the fix. Ask yourself, **is there anything in your PR that you ' - 'feel it is important we not accidentally revert back to how it was ' - 'before your fix?**' - '\n\n' - '__Reviewers__: Read the [Tree Hygiene page]' - '(https://github.com/flutter/flutter/wiki/Tree-hygiene#how-to-review-code) ' - 'and make sure this patch meets those guidelines before LGTMing.'; - - String get flutterGoldPending => 'Waiting for all other checks to be successful before querying Gold.'; - - String get flutterGoldSuccess => 'All golden file tests have passed.'; - - String get flutterGoldChanges => 'Image changes have been found for ' - 'this pull request.'; - - String get flutterGoldStalePR => 'This pull request executed golden file ' - 'tests, but it has not been updated in a while (20+ days). Test results from ' - 'Gold expire after as many days, so this pull request will need to be ' - 'updated with a fresh commit in order to get results from Gold.'; - - String get flutterGoldDraftChange => 'This pull request has been changed to a ' - 'draft. The currently pending flutter-gold status will not be able ' - 'to resolve until a new commit is pushed or the change is marked ready for ' - 'review again.'; - - String flutterGoldInitialAlert(String url) => 'Golden file changes have been found for this pull ' - 'request. Click [here to view and triage]($url) ' - '(e.g. because this is an intentional change).\n\n' - 'If you are still iterating on this change and are not ready to ' - 'resolve the images on the Flutter Gold dashboard, consider marking this PR ' - 'as a draft pull request above. You will still be able to view image results ' - 'on the dashboard, commenting will be silenced, and the check will not try to resolve itself until ' - 'marked ready for review.\n\n'; - - String flutterGoldFollowUpAlert(String url) => 'Golden file changes are available for triage from new commit, ' - 'Click [here to view]($url).\n\n'; - - String flutterGoldAlertConstant(gh.RepositorySlug slug) { - if (slug == Config.flutterSlug) { - return '\n\nFor more guidance, visit ' - '[Writing a golden file test for `package:flutter`](https://github.com/flutter/flutter/wiki/Writing-a-golden-file-test-for-package:flutter).\n\n' - '__Reviewers__: Read the [Tree Hygiene page](https://github.com/flutter/flutter/wiki/Tree-hygiene#how-to-review-code) ' - 'and make sure this patch meets those guidelines before LGTMing.\n\n'; - } - return ''; - } - - String flutterGoldCommentID(gh.PullRequest pr) => - '_Changes reported for pull request #${pr.number} at sha ${pr.head!.sha}_\n\n'; - - /// Post submit service account email used by LUCI swarming tasks. - static const String luciProdAccount = 'flutter-prod-builder@chops-service-accounts.iam.gserviceaccount.com'; - - /// Internal Google service account used to surface FRoB results. - static const String frobAccount = 'flutter-roll-on-borg@flutter-roll-on-borg.google.com.iam.gserviceaccount.com'; - - /// Service accounts used for PubSub messages. - static const Set allowedPubsubServiceAccounts = { - 'flutter-devicelab@flutter-dashboard.iam.gserviceaccount.com', - 'flutter-dashboard@appspot.gserviceaccount.com', - }; - - int get maxTaskRetries => 2; - - /// Max retries for Luci builder with infra failure. - int get maxLuciTaskRetries => 2; - - /// The default number of commit shown in flutter build dashboard. - int get commitNumber => 30; - - KeyHelper get keyHelper => KeyHelper(applicationContext: context.applicationContext); - - // Default number of commits to return for benchmark dashboard. - int /*!*/ get maxRecords => 50; - - // Delay between consecutive GitHub deflake request calls. - Duration get githubRequestDelay => const Duration(seconds: 1); - - // Repository status context for github status. - String get flutterBuild => 'flutter-build'; - - // Repository status description for github status. - String get flutterBuildDescription => 'Tree is currently broken. Please do not merge this ' - 'PR unless it contains a fix for the tree.'; - - static gh.RepositorySlug get cocoonSlug => gh.RepositorySlug('flutter', 'cocoon'); - static gh.RepositorySlug get engineSlug => gh.RepositorySlug('flutter', 'engine'); - static gh.RepositorySlug get flutterSlug => gh.RepositorySlug('flutter', 'flutter'); - static gh.RepositorySlug get packagesSlug => gh.RepositorySlug('flutter', 'packages'); - - /// Flutter recipes is hosted on Gerrit instead of GitHub. - static gh.RepositorySlug get recipesSlug => gh.RepositorySlug('flutter', 'recipes'); - - String get waitingForTreeToGoGreenLabelName => 'waiting for tree to go green'; - - /// The names of autoroller accounts for the repositories. - /// - /// These accounts should not need reviews before merging. See - /// https://github.com/flutter/flutter/wiki/Autorollers - Set get rollerAccounts => const { - 'skia-flutter-autoroll', - 'engine-flutter-autoroll', - 'dependabot', - 'dependabot[bot]', - }; - - Future generateJsonWebToken() async { - final String privateKey = await githubPrivateKey; - final String publicKey = await githubPublicKey; - final JWTBuilder builder = JWTBuilder(); - final DateTime now = DateTime.now(); - builder - ..issuer = await githubAppId - ..issuedAt = now - ..expiresAt = now.add(const Duration(minutes: 10)); - final JWTRsaSha256Signer signer = JWTRsaSha256Signer(privateKey: privateKey, publicKey: publicKey); - final JWT signedToken = builder.getSignedToken(signer); - return signedToken.toString(); - } - - Future generateGithubToken(gh.RepositorySlug slug) async { - // GitHub's secondary rate limits are run into very frequently when making auth tokens. - final Uint8List? cacheValue = await _cache.getOrCreateWithLocking( - configCacheName, - 'githubToken-${slug.fullName}', - createFn: () => _generateGithubToken(slug), - // Tokens are minted for 10 minutes - ttl: const Duration(minutes: 8), - ); - - return String.fromCharCodes(cacheValue!); - } - - Future _generateGithubToken(gh.RepositorySlug slug) async { - final Map appInstallations = await githubAppInstallations; - final String? appInstallation = appInstallations[slug.fullName]['installation_id'] as String?; - final String jsonWebToken = await generateJsonWebToken(); - final Map headers = { - 'Authorization': 'Bearer $jsonWebToken', - 'Accept': 'application/vnd.github.machine-man-preview+json', - }; - final Uri githubAccessTokensUri = Uri.https('api.github.com', 'app/installations/$appInstallation/access_tokens'); - final http.Response response = await http.post(githubAccessTokensUri, headers: headers); - final Map jsonBody = jsonDecode(response.body) as Map; - if (jsonBody.containsKey('token') == false) { - log.warning(response.body); - throw Exception('generateGitHubToken failed to get token from Github for repo=${slug.fullName}'); - } - final String token = jsonBody['token'] as String; - log.fine('Generated a new GitHub token for ${slug.fullName}'); - return Uint8List.fromList(token.codeUnits); - } - - Future createGitHubClient({gh.PullRequest? pullRequest, gh.RepositorySlug? slug}) async { - slug ??= pullRequest!.base!.repo!.slug(); - final String githubToken = await generateGithubToken(slug); - return createGitHubClientWithToken(githubToken); - } - - gh.GitHub createGitHubClientWithToken(String token) { - return gh.GitHub(auth: gh.Authentication.withToken(token)); - } - - Future createGitHubGraphQLClient() async { - final HttpLink httpLink = HttpLink( - 'https://api.github.com/graphql', - defaultHeaders: { - 'Accept': 'application/vnd.github.antiope-preview+json', - }, - ); - - final String token = await githubOAuthToken; - final AuthLink authLink = AuthLink( - getToken: () async => 'Bearer $token', - ); - - return GraphQLClient( - cache: GraphQLCache(), - link: authLink.concat(httpLink), - ); - } - - Future createBigQueryService() async { - final AccessClientProvider accessClientProvider = AccessClientProvider(); - return BigqueryService(accessClientProvider); - } - - Future createTabledataResourceApi() async { - return (await createBigQueryService()).defaultTabledata(); - } - - /// Default GitHub service when the repository does not matter. - /// - /// Internally uses the framework repo for OAuth. - Future createDefaultGitHubService() async { - return createGithubService(flutterSlug); - } - - Future createGithubService(gh.RepositorySlug slug) async { - final gh.GitHub github = await createGitHubClient(slug: slug); - return GithubService(github); - } - - GithubService createGithubServiceWithToken(String token) { - final gh.GitHub github = createGitHubClientWithToken(token); - return GithubService(github); - } -} diff --git a/app_dart/lib/src/service/datastore.dart b/app_dart/lib/src/service/datastore.dart deleted file mode 100644 index 73ece36ea..000000000 --- a/app_dart/lib/src/service/datastore.dart +++ /dev/null @@ -1,338 +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:math'; - -import 'package:cocoon_service/src/model/luci/buildbucket.dart'; -import 'package:cocoon_service/src/request_handling/exceptions.dart'; -import 'package:gcloud/datastore.dart' as gcloud_datastore; -import 'package:gcloud/db.dart'; -import 'package:github/github.dart' show RepositorySlug, PullRequest; -import 'package:grpc/grpc.dart'; -import 'package:meta/meta.dart'; -import 'package:retry/retry.dart'; - -import '../model/appengine/branch.dart'; -import '../model/appengine/commit.dart'; -import '../model/appengine/github_build_status_update.dart'; -import '../model/appengine/github_gold_status_update.dart'; -import '../model/appengine/stage.dart'; -import '../model/appengine/task.dart'; -import '../service/logging.dart'; -import 'config.dart'; - -/// Per the docs in [DatastoreDB.withTransaction], only 5 entity groups can -/// be touched in any given transaction, or the backing datastore will throw -/// an error. -const int defaultMaxEntityGroups = 5; - -/// This number inherits from old GO backend, and is upto change if necessary. -const int defaultTimeSeriesValuesNumber = 1500; - -/// Function signature for a [DatastoreService] provider. -typedef DatastoreServiceProvider = DatastoreService Function(DatastoreDB db); - -/// Function signature that will be executed with retries. -typedef RetryHandler = Function(); - -/// Runs a db transaction with retries. -/// -/// It uses quadratic backoff starting with 200ms and 3 max attempts. -/// for context please read https://github.com/flutter/flutter/issues/54615. -Future runTransactionWithRetries(RetryHandler retryHandler, {RetryOptions? retryOptions}) { - final RetryOptions r = retryOptions ?? - const RetryOptions( - maxDelay: Duration(seconds: 10), - maxAttempts: 3, - ); - return r.retry( - retryHandler, - retryIf: (Exception e) => e is gcloud_datastore.TransactionAbortedError || e is GrpcError, - ); -} - -/// Service class for interacting with App Engine cloud datastore. -/// -/// This service exists to provide an API for common datastore queries made by -/// the Cocoon backend. -@immutable -class DatastoreService { - /// Creates a new [DatastoreService]. - /// - /// The [db] argument must not be null. - const DatastoreService(this.db, this.maxEntityGroups, {RetryOptions? retryOptions}) - : retryOptions = retryOptions ?? - const RetryOptions( - maxDelay: Duration(seconds: 10), - maxAttempts: 3, - ); - - /// Maximum number of entity groups to process at once. - final int maxEntityGroups; - - /// The backing [DatastoreDB] object. - final DatastoreDB db; - - /// Transaction retry configurations. - final RetryOptions retryOptions; - - /// Creates and returns a [DatastoreService] using [db] and [maxEntityGroups]. - static DatastoreService defaultProvider(DatastoreDB db) { - return DatastoreService(db, defaultMaxEntityGroups); - } - - /// Queries for recent commits. - /// - /// The [limit] argument specifies the maximum number of commits to retrieve. - /// - /// The returned commits will be ordered by most recent [Commit.timestamp]. - Stream queryRecentCommits({ - int limit = 100, - int? timestamp, - String? branch, - required RepositorySlug slug, - }) { - timestamp ??= DateTime.now().millisecondsSinceEpoch; - branch ??= Config.defaultBranch(slug); - final Query query = db.query() - ..limit(limit) - ..filter('repository =', slug.fullName) - ..filter('branch =', branch) - ..order('-timestamp') - ..filter('timestamp <', timestamp); - return query.run(); - } - - Stream queryBranches() { - final Query query = db.query(); - return query.run(); - } - - /// Queries for recent [Task] by name. - /// - /// The [limit] argument specifies the maximum number of tasks to retrieve. - /// - /// The returned tasks will be ordered by most recent to oldest. - Stream queryRecentTasksByName({ - int limit = 100, - required String name, - }) { - final Query query = db.query() - ..limit(limit) - ..filter('name =', name) - ..order('-createTimestamp'); - return query.run(); - } - - /// Queries for recent tasks that meet the specified criteria. - /// - /// Since each task belongs to a commit, this query implicitly includes a - /// query of the most recent N commits (see [queryRecentCommits]). The - /// [commitLimit] argument specifies how many commits to consider when - /// retrieving the list of recent tasks. - /// - /// If [taskName] is specified, only tasks whose [Task.name] matches the - /// specified value will be returned. By default, tasks will be returned - /// regardless of their name. - /// - /// The returned tasks will be ordered by most recent [Commit.timestamp] - /// first, then by most recent [Task.createTimestamp]. - Stream queryRecentTasks({ - String? taskName, - int commitLimit = 20, - String? branch, - required RepositorySlug slug, - }) async* { - await for (Commit commit in queryRecentCommits(limit: commitLimit, branch: branch, slug: slug)) { - final Query query = db.query(ancestorKey: commit.key)..order('-createTimestamp'); - if (taskName != null) { - query.filter('name =', taskName); - } - yield* query.run().map((Task task) => FullTask(task, commit)); - } - } - - /// Finds all tasks owned by the specified [commit] and partitions them into - /// stages. - /// - /// The returned list of stages will be ordered by the natural ordering of - /// [Stage]. - Future> queryTasksGroupedByStage(Commit commit) async { - final Query query = db.query(ancestorKey: commit.key)..order('-stageName'); - final Map stages = {}; - await for (Task task in query.run()) { - if (!stages.containsKey(task.stageName)) { - stages[task.stageName] = StageBuilder() - ..commit = commit - ..name = task.stageName; - } - stages[task.stageName]!.tasks.add(task); - } - final List result = stages.values.map((StageBuilder stage) => stage.build()).toList(); - return result..sort(); - } - - Future queryLastStatusUpdate( - RepositorySlug slug, - PullRequest pr, - ) async { - final Query query = db.query() - ..filter('repository =', slug.fullName) - ..filter('pr =', pr.number) - ..filter('head =', pr.head!.sha); - final List previousStatusUpdates = await query.run().toList(); - - if (previousStatusUpdates.isEmpty) { - return GithubBuildStatusUpdate( - key: db.emptyKey.append(GithubBuildStatusUpdate), - repository: slug.fullName, - pr: pr.number!, - head: pr.head!.sha, - updates: 0, - updateTimeMillis: DateTime.now().millisecondsSinceEpoch, - ); - } else { - /// Duplicate cases rarely happen. It happens only when race condition - /// occurs in app engine. When multiple records exist, the latest one - /// is returned. - if (previousStatusUpdates.length > 1) { - return previousStatusUpdates.reduce( - (GithubBuildStatusUpdate current, GithubBuildStatusUpdate next) => - current.updateTimeMillis! < next.updateTimeMillis! ? next : current, - ); - } - return previousStatusUpdates.single; - } - } - - Future queryLastGoldUpdate( - RepositorySlug slug, - PullRequest pr, - ) async { - final Query query = db.query() - ..filter('repository =', slug.fullName) - ..filter('pr =', pr.number); - final List previousStatusUpdates = await query.run().toList(); - - if (previousStatusUpdates.isEmpty) { - return GithubGoldStatusUpdate( - pr: pr.number!, - head: '', - status: '', - updates: 0, - description: '', - repository: slug.fullName, - ); - } else { - if (previousStatusUpdates.length > 1) { - throw StateError('GithubGoldStatusUpdate should have no more than one entry on ' - 'repository ${slug.fullName}, pr ${pr.number}.'); - } - return previousStatusUpdates.single; - } - } - - /// Shards [rows] into several sublists of size [maxEntityGroups]. - Future>>> shard(List> rows) async { - final List>> shards = >>[]; - for (int i = 0; i < rows.length; i += maxEntityGroups) { - shards.add(rows.sublist(i, i + min(rows.length - i, maxEntityGroups))); - } - return shards; - } - - /// Inserts [rows] into datastore sharding the inserts if needed. - Future insert(List> rows) async { - final List>> shards = await shard(rows); - for (List> shard in shards) { - await runTransactionWithRetries( - () async { - await db.withTransaction((Transaction transaction) async { - transaction.queueMutations(inserts: shard); - await transaction.commit(); - }); - }, - retryOptions: retryOptions, - ); - } - } - - /// Looks up registers by [keys]. - Future> lookupByKey>(List> keys) async { - List results = []; - await runTransactionWithRetries( - () async { - await db.withTransaction((Transaction transaction) async { - results = await transaction.lookup(keys); - await transaction.commit(); - }); - }, - retryOptions: retryOptions, - ); - return results; - } - - /// Looks up registers by value using a single [key]. - Future lookupByValue>(Key key, {T Function()? orElse}) async { - late T result; - await runTransactionWithRetries( - () async { - await db.withTransaction((Transaction transaction) async { - result = await db.lookupValue(key, orElse: orElse); - await transaction.commit(); - }); - }, - retryOptions: retryOptions, - ); - return result; - } - - /// Runs a function inside a transaction providing a [Transaction] parameter. - Future withTransaction(Future Function(Transaction) handler) async { - T? result; - await runTransactionWithRetries( - () async { - await db.withTransaction((Transaction transaction) async { - result = await handler(transaction); - }); - }, - retryOptions: retryOptions, - ); - return result; - } - - Future getTaskFromBuildbucketBuild( - Build build, { - String? customName, - }) async { - log.fine('Generating commit key from buildbucket build: ${build.toString()}'); - - final String repository = build.input!.gitilesCommit!.project!.split('/')[1]; - log.fine('Repository: $repository'); - - 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 String id = '${slug.fullName}/$branch/$hash'; - final Key commitKey = db.emptyKey.append(Commit, id: id); - - try { - return await Task.fromDatastore( - datastore: this, - commitKey: commitKey, - name: customName ?? build.builderId.builder, - ); - } on InternalServerError catch (e) { - log.warning('Failed to find an existing task for the buildbucket build: ${e.toString()}'); - return null; - } - } -} diff --git a/app_dart/lib/src/service/exceptions.dart b/app_dart/lib/src/service/exceptions.dart deleted file mode 100644 index eec4c173b..000000000 --- a/app_dart/lib/src/service/exceptions.dart +++ /dev/null @@ -1,23 +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. - -class NoBuildFoundException implements Exception { - /// Create a custom exception for no build found Errors. - NoBuildFoundException(this.cause); - - final String cause; - - @override - String toString() => cause; -} - -class UnfinishedBuildException implements Exception { - /// Create a custom exception for an unfinished buildbucket build - UnfinishedBuildException(this.cause); - - final String cause; - - @override - String toString() => cause; -} diff --git a/app_dart/lib/src/service/gerrit_service.dart b/app_dart/lib/src/service/gerrit_service.dart deleted file mode 100644 index ac9cd42f3..000000000 --- a/app_dart/lib/src/service/gerrit_service.dart +++ /dev/null @@ -1,180 +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/src/request_handling/exceptions.dart'; -import 'package:github/github.dart'; -import 'package:googleapis_auth/auth_io.dart'; -import 'package:http/http.dart' as http; -import 'package:http/retry.dart'; -import 'package:meta/meta.dart'; - -import '../model/gerrit/commit.dart'; -import 'config.dart'; -import 'logging.dart'; - -/// Communicates with gerrit APIs https://gerrit-review.googlesource.com/Documentation/rest-api-projects.html -/// to get information about projects hosted in Git on Borg. -class GerritService { - GerritService({ - required this.config, - http.Client? httpClient, - @visibleForTesting this.authClientProvider = clientViaApplicationDefaultCredentials, - @visibleForTesting this.retryDelay, - }) : httpClient = httpClient ?? http.Client(); - - final Config config; - final http.Client httpClient; - - final Duration? retryDelay; - - /// Provider for generating a [http.Client] that is authenticated to make calls to GCP services. - final Future Function({ - http.Client? baseClient, - required List scopes, - }) authClientProvider; - - /// Gets the branches from a remote git repository using the gerrit APIs. - /// - /// [filterRegex] a regular expression string to filter the branches list to - /// the ones matching the regex. - /// - /// See more: - /// * https://gerrit-review.googlesource.com/Documentation/rest-api-projects.html#list-branches - Future> branches(String repo, String project, {String? filterRegex}) async { - final Map queryParameters = { - if (filterRegex != null && filterRegex.isNotEmpty) 'r': filterRegex, - }; - final Uri url = Uri.https(repo, 'projects/$project/branches', queryParameters); - final List response = await _getJson(url) as List; - - final List branches = []; - - final Iterable> json = response.map((dynamic e) => e as Map); - for (Map element in json) { - branches.add(element['ref'] as String); - } - - return branches; - } - - /// Gets the commit log for a project-branch pair. - Future> commits(RepositorySlug slug, String branch) async { - final Uri url = - Uri.https('${slug.owner}.googlesource.com', '${slug.name}/+log/refs/heads/$branch', { - 'format': 'json', - }); - final Map response = await _getJson(url) as Map; - final List commitsJson = response['log'] as List; - - return commitsJson.map((dynamic part) => GerritCommit.fromJson(part as Map)); - } - - /// Finds a commit on a GoB mirror using the GitHub [slug] and commit [sha]. - /// - /// The [slug] will be validated by checking if it represents a presubmit or postsubmit supported repo. - Future findMirroredCommit(RepositorySlug slug, String sha) async { - if (!config.supportedRepos.contains(slug)) return null; - final gobMirrorName = 'mirrors/${slug.name}'; - return getCommit(RepositorySlug(slug.owner, gobMirrorName), sha); - } - - /// Gets the commit info from Gob. - /// - /// See more: - /// * https://gerrit-review.googlesource.com/Documentation/rest-api-projects.html#get-commit - Future getCommit(RepositorySlug slug, String commitId) async { - // URL encode because "mirrors/flutter" should be "mirrors%2Fflutter". - final String projectName = Uri.encodeComponent(slug.name); - // Uses [Uri] constructor instead of [Uri.https], where the latter uses [unencodedPath]. - // Our mirror repo, say [mirrors/cocoon], will not be encoded - // as [mirrors%2Fcocoon] if injected directly. On the other hand, if we inject an encoded - // version [mirrors%2Fcocoon], [Uri.https] will translate that to [mirrors%252Fcocoon]. - // Neither works with [Uri.https]. - final Uri url = Uri( - scheme: 'https', - host: '${slug.owner}-review.googlesource.com', - path: 'projects/$projectName/commits/$commitId', - ); - log.info('Gerrit get-commit url: $url'); - - final http.Response response = await _get(url); - log.info('Gob commit response for commit $commitId: ${response.body}'); - if (!_responseIsAcceptable(response)) return null; - - final String jsonBody = _stripXssToken(response.body); - final Map json = jsonDecode(jsonBody) as Map; - return GerritCommit.fromJson(json); - } - - /// Strips magic prefix from response body. - /// - /// To prevent against Cross Site Script Inclusion (XSSI) attacks, the JSON response body starts with a magic prefix line that - /// must be stripped before feeding the rest of the response body to a JSON parser. The magic prefix is ")]}'". - String _stripXssToken(String body) { - return body.replaceRange(0, 4, ''); - } - - /// Creates a new branch. - /// - /// See more: - /// * https://gerrit-review.googlesource.com/Documentation/rest-api-projects.html#create-branch - Future createBranch(RepositorySlug slug, String branchName, String revision) async { - log.info('Creating branch $branchName at $revision'); - final Uri url = Uri.https('${slug.owner}-review.googlesource.com', 'projects/${slug.name}/branches/$branchName'); - final Map response = await _put( - url, - body: revision, - ) as Map; - log.info(response); - if (response['revision'] != revision) { - throw const InternalServerError('Failed to create branch'); - } - log.info('Created branch $branchName'); - } - - Future _getJson(Uri url) async { - final RetryClient client = RetryClient(httpClient); - final http.Response response = await client.get(url); - final String jsonBody = _stripXssToken(response.body); - return jsonDecode(jsonBody) as dynamic; - } - - Future _get(Uri url) async { - final RetryClient client = RetryClient(httpClient); - return client.get(url); - } - - Future _put( - Uri url, { - Object? body, - }) async { - final http.Client authClient = await authClientProvider(baseClient: httpClient, scopes: []); - // GoB replicas may not have all the Flutter state, and can require several retries - final http.Client client = RetryClient( - authClient, - when: (http.BaseResponse response) => _responseIsAcceptable(response) == false, - delay: (int attempt) => retryDelay ?? const Duration(seconds: 1) * attempt, - ); - final http.Response response = await client.put( - url, - body: body, - ); - if (_responseIsAcceptable(response) == false) { - throw InternalServerError('Gerrit returned ${response.statusCode} which is not 200 or 202'); - } - log.info('Sent PUT to $url'); - log.info(response.body); - // Remove XSS token - final String jsonBody = _stripXssToken(response.body); - log.info(jsonBody); - return jsonDecode(jsonBody); - } - - bool _responseIsAcceptable(http.BaseResponse response) => - response.statusCode == HttpStatus.ok || response.statusCode == HttpStatus.accepted; -} diff --git a/app_dart/lib/src/service/github_checks_service.dart b/app_dart/lib/src/service/github_checks_service.dart deleted file mode 100644 index 9a962e24f..000000000 --- a/app_dart/lib/src/service/github_checks_service.dart +++ /dev/null @@ -1,238 +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' as github; -import 'package:github/hooks.dart'; - -import '../foundation/github_checks_util.dart'; -import '../model/luci/buildbucket.dart'; -import '../model/luci/push_message.dart' as push_message; -import 'config.dart'; -import 'github_service.dart'; -import 'logging.dart'; -import 'luci_build_service.dart'; -import 'scheduler.dart'; - -const String kGithubSummary = ''' -**[Understanding a LUCI build failure](https://github.com/flutter/flutter/wiki/Understanding-a-LUCI-build-failure)** - -'''; - -/// Controls triggering builds and updating their status in the Github UI. -class GithubChecksService { - GithubChecksService(this.config, {GithubChecksUtil? githubChecksUtil}) - : githubChecksUtil = githubChecksUtil ?? const GithubChecksUtil(); - - Config config; - GithubChecksUtil githubChecksUtil; - - static Set failedStatesSet = { - github.CheckRunConclusion.cancelled, - github.CheckRunConclusion.failure, - }; - - /// Takes a [CheckSuiteEvent] and trigger all the relevant builds if this is a - /// new commit or only failed builds if the event was generated by a click on - /// the re-run all button in the Github UI. - /// Relevant API docs: - /// https://docs.github.com/en/rest/reference/checks#create-a-check-suite - /// https://docs.github.com/en/rest/reference/checks#rerequest-a-check-suite - Future handleCheckSuite( - github.PullRequest pullRequest, - CheckSuiteEvent checkSuiteEvent, - Scheduler scheduler, - ) async { - switch (checkSuiteEvent.action) { - case 'requested': - // Trigger all try builders. - log.info('Check suite request for pull request ${pullRequest.number}, ${pullRequest.title}'); - await scheduler.triggerPresubmitTargets( - pullRequest: pullRequest, - ); - break; - case 'rerequested': - log.info('Check suite re-request for pull request ${pullRequest.number}, ${pullRequest.title}'); - pullRequest.head = github.PullRequestHead(sha: checkSuiteEvent.checkSuite?.headSha); - return scheduler.retryPresubmitTargets( - pullRequest: pullRequest, - checkSuiteEvent: checkSuiteEvent, - ); - } - } - - /// Updates the Github build status using a [BuildPushMessage] sent by LUCI in - /// a pub/sub notification. - /// Relevant APIs: - /// https://docs.github.com/en/rest/reference/checks#update-a-check-run - Future updateCheckStatus( - push_message.BuildPushMessage buildPushMessage, - LuciBuildService luciBuildService, - github.RepositorySlug slug, { - bool rescheduled = false, - }) async { - final push_message.Build? build = buildPushMessage.build; - if (buildPushMessage.userData.isEmpty) { - return false; - } - - if (!buildPushMessage.userData.containsKey('check_run_id') || - !buildPushMessage.userData.containsKey('repo_owner') || - !buildPushMessage.userData.containsKey('repo_name')) { - log.severe( - 'UserData did not contain check_run_id,' - 'repo_owner, or repo_name: ${buildPushMessage.userData}', - ); - return false; - } - github.CheckRunStatus status = statusForResult(build!.status); - // Only `id` and `name` in the CheckRun are needed. - // Instead of making an API call to get the details of each check run, we - // generate the check run with only necessary info. - final github.CheckRun checkRun = github.CheckRun.fromJson({ - 'id': buildPushMessage.userData['check_run_id'] as int?, - 'status': status, - 'check_suite': const {'id': null}, - 'started_at': build.createdTimestamp.toString(), - 'conclusion': null, - 'name': build.buildParameters!['builder_name'], - }); - github.CheckRunConclusion? conclusion = - (buildPushMessage.build!.result != null) ? conclusionForResult(buildPushMessage.build!.result) : null; - final String? url = buildPushMessage.build!.url; - github.CheckRunOutput? output; - // If status has completed with failure then provide more details. - if (taskFailed(buildPushMessage)) { - if (rescheduled) { - status = github.CheckRunStatus.queued; - conclusion = null; - output = github.CheckRunOutput( - title: checkRun.name!, - summary: 'Note: this is an auto rerun. The timestamp above is based on the first attempt of this check run.', - ); - } else { - final Build buildbucketBuild = - await luciBuildService.getBuildById(buildPushMessage.build!.id, fields: 'id,builder,summaryMarkdown'); - output = github.CheckRunOutput( - title: checkRun.name!, - summary: getGithubSummary(buildbucketBuild.summaryMarkdown), - ); - log.fine('Updating check run with output: [$output]'); - } - } - await githubChecksUtil.updateCheckRun( - config, - slug, - checkRun, - status: status, - conclusion: conclusion, - detailsUrl: url, - output: output, - ); - return true; - } - - /// Check if task has completed with failure. - bool taskFailed(push_message.BuildPushMessage buildPushMessage) { - final push_message.Build? build = buildPushMessage.build; - final github.CheckRunStatus status = statusForResult(build!.status); - final github.CheckRunConclusion? conclusion = - (buildPushMessage.build!.result != null) ? conclusionForResult(buildPushMessage.build!.result) : null; - return status == github.CheckRunStatus.completed && failedStatesSet.contains(conclusion); - } - - /// Returns current reschedule attempt. - /// - /// It returns 1 if this is the first run, and +1 with each reschedule. - int currentAttempt(push_message.BuildPushMessage buildPushMessage) { - final push_message.Build build = buildPushMessage.build!; - if (build.tagsByName('current_attempt').isEmpty) { - return 1; - } else { - return int.parse(build.tagsByName('current_attempt').single); - } - } - - /// Appends triage wiki page to `summaryMarkdown` from LUCI build so that people can easily - /// reference from github check run page. - String getGithubSummary(String? summary) { - if (summary == null) { - return '${kGithubSummary}Empty summaryMarkdown'; - } - // This is an imposed GitHub limit - const int checkSummaryLimit = 65535; - // This is to give buffer room incase GitHub lowers the amount. - const int checkSummaryBufferLimit = checkSummaryLimit - 10000 - kGithubSummary.length; - // Return the last [checkSummaryBufferLimit] characters as they are likely the most relevant. - if (summary.length > checkSummaryBufferLimit) { - final String truncatedSummary = summary.substring(summary.length - checkSummaryBufferLimit); - summary = '[TRUNCATED...] $truncatedSummary'; - } - return '$kGithubSummary$summary'; - } - - /// Transforms a [push_message.Result] to a [github.CheckRunConclusion]. - /// Relevant APIs: - /// https://developer.github.com/v3/checks/runs/#check-runs - github.CheckRunConclusion conclusionForResult(push_message.Result? result) { - switch (result) { - case push_message.Result.canceled: - // Set conclusion cancelled as a failure to ensure developers can retry - // tasks when builds timeout. - return github.CheckRunConclusion.failure; - case push_message.Result.failure: - return github.CheckRunConclusion.failure; - case push_message.Result.success: - return github.CheckRunConclusion.success; - case null: - throw StateError('unreachable'); - } - } - - /// Transforms a [push_message.Status] to a [github.CheckRunStatus]. - /// Relevant APIs: - /// https://developer.github.com/v3/checks/runs/#check-runs - github.CheckRunStatus statusForResult(push_message.Status? status) { - switch (status) { - case push_message.Status.completed: - return github.CheckRunStatus.completed; - case push_message.Status.scheduled: - return github.CheckRunStatus.queued; - case push_message.Status.started: - return github.CheckRunStatus.inProgress; - case null: - throw StateError('unreachable'); - } - } - - /// Given a [headSha] and [checkSuiteId], finds the [PullRequest] that matches. - Future findMatchingPullRequest( - github.RepositorySlug slug, - String headSha, - int checkSuiteId, - ) async { - final GithubService githubService = await config.createDefaultGitHubService(); - - // There could be multiple PRs that have the same [headSha] commit. - final List prIssues = await githubService.searchIssuesAndPRs(slug, '$headSha type:pr'); - - for (final prIssue in prIssues) { - final int prNumber = prIssue.number; - - // Each PR can have multiple check suites. - final List checkSuites = await githubChecksUtil.listCheckSuitesForRef( - githubService.github, - slug, - ref: 'refs/pull/$prNumber/head', - ); - - // Use check suite ID equality to verify that we have iterated to the correct PR. - final bool doesPrIncludeMatchingCheckSuite = checkSuites.any((checkSuite) => checkSuite.id! == checkSuiteId); - if (doesPrIncludeMatchingCheckSuite) { - return githubService.getPullRequest(slug, prNumber); - } - } - - return null; - } -} diff --git a/app_dart/lib/src/service/github_service.dart b/app_dart/lib/src/service/github_service.dart deleted file mode 100644 index 19d605ec6..000000000 --- a/app_dart/lib/src/service/github_service.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:math'; - -import 'package:github/github.dart'; -import 'package:http/http.dart'; - -import '../service/logging.dart'; - -class GithubService { - GithubService(this.github); - - final GitHub github; - static final Map headers = {'Accept': 'application/vnd.github.groot-preview+json'}; - static const String kRefsPrefix = 'refs/heads/'; - - /// Return commits unique to [branch] for the repository [slug]. - /// - /// When [lastCommitTimestampMills] equals 0, it means a new release branch is - /// found and only the branched commit will be returned for now, though the - /// rare case that multiple commits exist. For other cases, it returns all - /// newer commits since [lastCommitTimestampMills]. - Future> listBranchedCommits( - RepositorySlug slug, - String branch, - int? lastCommitTimestampMills, - ) async { - ArgumentError.checkNotNull(slug); - final PaginationHelper paginationHelper = PaginationHelper(github); - - /// The [pages] defines the number of pages of returned http request - /// results. Return only one page when this is a new branch. Otherwise - /// it will return all commits prior to this release branch commit, - /// leading to heavy workload. - int? pages; - if (lastCommitTimestampMills == null || lastCommitTimestampMills == 0) { - pages = 1; - } - - List> commits = >[]; - - /// [lastCommitTimestamp+1] excludes last commit itself. - /// Github api url: https://developer.github.com/v3/repos/commits/#list-commits - await for (Response response in paginationHelper.fetchStreamed( - 'GET', - '/repos/${slug.fullName}/commits', - params: { - 'sha': branch, - 'since': DateTime.fromMillisecondsSinceEpoch((lastCommitTimestampMills ?? 0) + 1).toUtc().toIso8601String(), - }, - pages: pages, - headers: headers, - )) { - commits.addAll((json.decode(response.body) as List).cast>()); - } - - /// When a release branch is first detected only the most recent commit would be needed. - /// - /// If for the worst case, a new release branch consists of a useful cherry pick commit - /// which should be considered as well, here is the todo. - // TODO(keyonghan): https://github.com/flutter/flutter/issues/59275 - if (lastCommitTimestampMills == 0) { - commits = commits.take(1).toList(); - } - - return commits.map((Map commit) { - return RepositoryCommit() - ..sha = commit['sha'] as String? - ..author = (User() - ..login = commit['author']['login'] as String? - ..avatarUrl = commit['author']['avatar_url'] as String?) - ..commit = (GitCommit() - ..message = commit['commit']['message'] as String? - ..committer = (GitCommitUser( - commit['commit']['author']['name'] as String?, - commit['commit']['author']['email'] as String?, - DateTime.parse(commit['commit']['author']['date'] as String), - ))); - }).toList(); - } - - /// List pull requests in the repository. - Future> listPullRequests(RepositorySlug slug, String? branch) { - ArgumentError.checkNotNull(slug); - return github.pullRequests - .list( - slug, - base: branch, - direction: 'desc', - sort: 'created', - state: 'open', - ) - .toList(); - } - - /// Creates a pull request against the `baseRef` in the `slug` repository. - /// - /// The `entries` contains the file changes in the created pull request. This - /// method creates a branch in the current user's forked repository to create - /// the pull request. The current user must have a forked repository from the - /// targeted slug, and the targeted slug must not be belong to current user. - Future createPullRequest( - RepositorySlug slug, { - required String title, - String? body, - String? commitMessage, - required GitReference baseRef, - List? entries, - }) async { - ArgumentError.checkNotNull(slug); - ArgumentError.checkNotNull(title); - - final RepositorySlug clientSlug = await _getCurrentUserSlug(slug.name); - final GitTree tree = await github.git.createTree(clientSlug, CreateGitTree(entries, baseTree: baseRef.object!.sha)); - final CurrentUser currentUser = (await _getCurrentUser())!; - final GitCommitUser commitUser = GitCommitUser(currentUser.name, currentUser.email, DateTime.now()); - final GitCommit commit = await github.git.createCommit( - clientSlug, - CreateGitCommit( - commitMessage, - tree.sha, - parents: [baseRef.object!.sha], - author: commitUser, - committer: commitUser, - ), - ); - final GitReference headRef = - await github.git.createReference(clientSlug, '$kRefsPrefix${_generateNewRef()}', commit.sha); - return github.pullRequests.create( - slug, - CreatePullRequest(title, '${clientSlug.owner}:${headRef.ref}', baseRef.ref, body: body), - ); - } - - /// Assigns a reviewer to the pull request in the repository. - /// - /// The `reviewer` contains the github login of the reviewer. - Future assignReviewer( - RepositorySlug slug, { - int? pullRequestNumber, - String? reviewer, - }) async { - const JsonEncoder encoder = JsonEncoder(); - await github.postJSON, PullRequest>( - '/repos/${slug.fullName}/pulls/$pullRequestNumber/requested_reviewers', - convert: (Map i) => PullRequest.fromJson(i), - body: encoder.convert({ - 'reviewers': [reviewer], - }), - ); - } - - /// Adds labels to an issue. - /// - /// A pull request is an issue. This works for pull requests as well. - Future> addIssueLabels( - RepositorySlug slug, - int issueNumber, - List labels, - ) async { - ArgumentError.checkNotNull(slug); - ArgumentError.checkNotNull(issueNumber); - ArgumentError.checkNotNull(labels); - return github.issues.addLabelsToIssue(slug, issueNumber, labels); - } - - /// Retrieves issues from the repository. - /// - /// Uses the `labels` to return the issues that have the labels. - /// - /// The `state` can be set `open`, `closed, or `all`. If it is set to `open`, - /// this method only returns issues that are currently open. If it is set to - /// `closed`, this method returns issues that are currently closed. The `all` - /// returns both closed and open issues. Defaults to `open`. - Future> listIssues( - RepositorySlug slug, { - List? labels, - String state = 'open', - }) { - ArgumentError.checkNotNull(slug); - return github.issues.listByRepo(slug, labels: labels, state: state).toList(); - } - - /// Get an issue with the issue number - Future? getIssue( - RepositorySlug slug, { - required int issueNumber, - }) { - ArgumentError.checkNotNull(slug); - ArgumentError.checkNotNull(issueNumber); - return github.issues.get(slug, issueNumber); - } - - /// Assign the issue to the assignee. - Future assignIssue( - RepositorySlug slug, { - required int issueNumber, - required String assignee, - }) async { - ArgumentError.checkNotNull(slug); - ArgumentError.checkNotNull(issueNumber); - ArgumentError.checkNotNull(assignee); - await github.issues.edit(slug, issueNumber, IssueRequest(assignee: assignee)); - } - - Future createIssue( - RepositorySlug slug, { - String? title, - String? body, - List? labels, - String? assignee, - }) async { - ArgumentError.checkNotNull(slug); - return github.issues.create( - slug, - IssueRequest(title: title, body: body, labels: labels, assignee: assignee), - ); - } - - Future createComment( - RepositorySlug slug, { - required int issueNumber, - required String body, - }) async { - ArgumentError.checkNotNull(slug); - ArgumentError.checkNotNull(issueNumber); - return github.issues.createComment(slug, issueNumber, body); - } - - Future> replaceLabelsForIssue( - RepositorySlug slug, { - required int issueNumber, - required List labels, - }) async { - ArgumentError.checkNotNull(slug); - ArgumentError.checkNotNull(issueNumber); - final Response response = await github.request( - 'PUT', - '/repos/${slug.fullName}/issues/$issueNumber/labels', - body: GitHubJson.encode(labels), - ); - final List body = jsonDecode(response.body) as List; - return body.map((dynamic it) => IssueLabel.fromJson(it as Map)).toList(); - } - - /// Returns changed files for a [PullRequest]. - /// - /// See more: - /// * https://developer.github.com/v3/pulls/#list-pull-requests-files - Future> listFiles(PullRequest pullRequest) async { - final List files = - await github.pullRequests.listFiles(pullRequest.base!.repo!.slug(), pullRequest.number!).toList(); - log.fine('List of files: $files'); - return files.map((PullRequestFile file) { - return file.filename!; - }).toList(); - } - - /// Gets the file content as UTF8 string of the file specified by the `path` - /// in the repository. - Future getFileContent(RepositorySlug slug, String path, {String? ref}) async { - ArgumentError.checkNotNull(slug); - ArgumentError.checkNotNull(path); - final RepositoryContents contents = await github.repositories.getContents(slug, path, ref: ref); - if (!contents.isFile) { - throw 'The path $path should point to a file, but it is not!'; - } - final String content = utf8.decode(base64.decode(contents.file!.content!.replaceAll('\n', ''))); - return content; - } - - /// Gets the reference of a specific branch in the repository. - Future getReference(RepositorySlug slug, String ref) { - ArgumentError.checkNotNull(slug); - ArgumentError.checkNotNull(ref); - return github.git.getReference(slug, ref); - } - - /// Returns JSON of the current GitHub API quota usage. - /// - /// This does not consume any API usage. - /// - /// Reference: - /// * https://docs.github.com/en/rest/reference/rate-limit - Future getRateLimit() => github.misc.getRateLimit(); - - CurrentUser? _currentUser; - - Future _getCurrentUser() async { - _currentUser ??= await github.users.getCurrentUser(); - return _currentUser; - } - - Future _getCurrentUserSlug(String repository) async { - return RepositorySlug((await _getCurrentUser())!.login!, repository); - } - - String _generateNewRef() { - const String chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz1234567890'; - final Random rnd = Random(); - return String.fromCharCodes(Iterable.generate(10, (_) => chars.codeUnitAt(rnd.nextInt(chars.length)))); - } - - /// Returns a [List] of [Issue]s that match the given [query]. - /// - /// The GitHub package uses the [Issue] object for both issue results and PRs. - /// - /// Reference: - /// * https://docs.github.com/en/rest/search?apiVersion=2022-11-28#search-issues-and-pull-requests - /// * https://docs.github.com/en/search-github/searching-on-github/searching-issues-and-pull-requests - Future> searchIssuesAndPRs( - RepositorySlug slug, - String query, { - String? sort, - int pages = 2, - }) { - return github.search - .issues( - Uri.encodeComponent('$query repo:${slug.fullName}'), - sort: sort, - pages: pages, - ) - .toList(); - } - - /// Retrieves a pull request with the given [number]. - /// - /// Reference: - /// * https://docs.github.com/en/rest/pulls/pulls?apiVersion=2022-11-28#get-a-pull-request - Future getPullRequest(RepositorySlug slug, int number) async { - return github.pullRequests.get(slug, number); - } -} diff --git a/app_dart/lib/src/service/logging.dart b/app_dart/lib/src/service/logging.dart deleted file mode 100644 index 1498eea42..000000000 --- a/app_dart/lib/src/service/logging.dart +++ /dev/null @@ -1,7 +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:logging/logging.dart'; - -final Logger log = Logger.root..level = Level.ALL; diff --git a/app_dart/lib/src/service/luci_build_service.dart b/app_dart/lib/src/service/luci_build_service.dart deleted file mode 100644 index fb23f4a91..000000000 --- a/app_dart/lib/src/service/luci_build_service.dart +++ /dev/null @@ -1,728 +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:math'; -import 'dart:typed_data'; - -import 'package:github/github.dart' as github; -import 'package:github/hooks.dart'; -import 'package:googleapis/pubsub/v1.dart'; - -import '../foundation/github_checks_util.dart'; -import '../foundation/utils.dart'; -import '../model/appengine/commit.dart'; -import '../model/appengine/task.dart'; -import '../model/ci_yaml/target.dart'; -import '../model/github/checks.dart' as cocoon_checks; -import '../model/luci/buildbucket.dart'; -import '../model/luci/push_message.dart' as push_message; -import '../request_handling/pubsub.dart'; -import '../service/datastore.dart'; -import '../service/logging.dart'; -import 'buildbucket.dart'; -import 'cache_service.dart'; -import 'config.dart'; -import 'exceptions.dart'; -import 'gerrit_service.dart'; - -const Set taskFailStatusSet = { - Task.statusInfraFailure, - Task.statusFailed, - Task.statusCancelled, -}; - -/// Class to interact with LUCI buildbucket to get, trigger -/// and cancel builds for github repos. It uses [config.luciTryBuilders] to -/// get the list of available builders. -class LuciBuildService { - LuciBuildService({ - required this.config, - required this.cache, - required this.buildBucketClient, - GithubChecksUtil? githubChecksUtil, - GerritService? gerritService, - this.pubsub = const PubSub(), - }) : githubChecksUtil = githubChecksUtil ?? const GithubChecksUtil(), - gerritService = gerritService ?? GerritService(config: config); - - BuildBucketClient buildBucketClient; - final CacheService cache; - Config config; - GithubChecksUtil githubChecksUtil; - GerritService gerritService; - - final PubSub pubsub; - - static const Set failStatusSet = {Status.canceled, Status.failure, Status.infraFailure}; - - static const int kBackfillPriority = 35; - static const int kDefaultPriority = 30; - static const int kRerunPriority = 29; - - /// Github labels have a max length of 100, so conserve chars here. - /// This is currently used by packages repo only. - /// See: https://github.com/flutter/flutter/issues/130076 - static const String githubBuildLabelPrefix = 'override:'; - static const String propertiesGithubBuildLabelName = 'overrides'; - - /// Name of the subcache to store luci build related values in redis. - static const String subCacheName = 'luci'; - - /// Shards [rows] into several sublists of size [maxEntityGroups]. - Future>> shard(List requests, int max) async { - final List> shards = >[]; - for (int i = 0; i < requests.length; i += max) { - shards.add(requests.sublist(i, i + min(requests.length - i, max))); - } - return shards; - } - - /// Returns an Iterable of try BuildBucket build for a given Github [slug], [sha], [builderName]. - Future> getTryBuilds( - github.RepositorySlug slug, - String sha, - String? builderName, - ) async { - final Map> tags = >{ - 'buildset': ['sha/git/$sha'], - 'user_agent': const ['flutter-cocoon'], - }; - return getBuilds(slug, sha, builderName, 'try', tags); - } - - /// Returns an Iterable of try Buildbucket [Build]s for a given [PullRequest]. - Future> getTryBuildsByPullRequest( - github.PullRequest pullRequest, - ) async { - final github.RepositorySlug slug = pullRequest.base!.repo!.slug(); - final Map> tags = >{ - 'buildset': ['pr/git/${pullRequest.number}'], - 'github_link': ['https://github.com/${slug.fullName}/pull/${pullRequest.number}'], - 'user_agent': const ['flutter-cocoon'], - }; - return getBuilds(slug, null, null, 'try', tags); - } - - /// Returns an Iterable of prod BuildBucket build for a given Github [slug], [commitSha], - /// [builderName] and [repo]. - Future> getProdBuilds( - github.RepositorySlug slug, - String commitSha, - String? builderName, - ) async { - final Map> tags = >{}; - return getBuilds(slug, commitSha, builderName, 'prod', tags); - } - - /// Returns an iterable of BuildBucket builds for a given Github [slug], [commitSha], - /// [builderName], [bucket] and [tags]. - Future> getBuilds( - github.RepositorySlug? slug, - String? commitSha, - String? builderName, - String bucket, - Map> tags, - ) async { - final BatchResponse batch = await buildBucketClient.batch( - BatchRequest( - requests: [ - Request( - searchBuilds: SearchBuildsRequest( - predicate: BuildPredicate( - builderId: BuilderId( - project: 'flutter', - bucket: bucket, - builder: builderName, - ), - tags: tags, - ), - fields: 'builds.*.id,builds.*.builder,builds.*.tags,builds.*.status,builds.*.input.properties', - ), - ), - ], - ), - ); - - log.info('Reponses from get builds batch request = ${batch.responses!.length}'); - for (Response response in batch.responses!) { - log.info('Found a response: ${response.toString()}'); - } - - final Iterable builds = batch.responses! - .map((Response response) => response.searchBuilds) - .expand((SearchBuildsResponse? response) => response!.builds ?? []); - return builds; - } - - /// Schedules presubmit [targets] on BuildBucket for [pullRequest]. - Future> scheduleTryBuilds({ - required List targets, - required github.PullRequest pullRequest, - CheckSuiteEvent? checkSuiteEvent, - }) async { - if (targets.isEmpty) { - return targets; - } - - final List requests = []; - final List branches = await gerritService.branches( - 'flutter-review.googlesource.com', - 'recipes', - filterRegex: 'flutter-.*|fuchsia.*', - ); - log.info('Available release branches: $branches'); - - final String sha = pullRequest.head!.sha!; - String cipdVersion = 'refs/heads/${pullRequest.base!.ref!}'; - cipdVersion = branches.contains(cipdVersion) ? cipdVersion : config.defaultRecipeBundleRef; - - for (Target target in targets) { - final github.CheckRun checkRun = await githubChecksUtil.createCheckRun( - config, - target.slug, - sha, - target.value.name, - ); - - final github.RepositorySlug slug = pullRequest.base!.repo!.slug(); - - final Map userData = { - 'builder_name': target.value.name, - 'check_run_id': checkRun.id, - 'commit_sha': sha, - 'commit_branch': pullRequest.base!.ref!.replaceAll('refs/heads/', ''), - }; - - final Map> tags = >{ - 'github_checkrun': [checkRun.id.toString()], - }; - - final Map properties = target.getProperties(); - properties.putIfAbsent('git_branch', () => pullRequest.base!.ref!.replaceAll('refs/heads/', '')); - - final List? labels = pullRequest.labels - ?.where((label) => label.name.startsWith(githubBuildLabelPrefix)) - .map((obj) => obj.name) - .toList(); - - if (labels != null && labels.isNotEmpty) { - properties[propertiesGithubBuildLabelName] = labels; - } - - requests.add( - Request( - scheduleBuild: _createPresubmitScheduleBuild( - slug: slug, - sha: pullRequest.head!.sha!, - //Use target.value.name here otherwise tests will die due to null checkRun.name. - checkName: target.value.name, - pullRequestNumber: pullRequest.number!, - cipdVersion: cipdVersion, - userData: userData, - properties: properties, - tags: tags, - dimensions: target.getDimensions(), - ), - ), - ); - } - - final Iterable> requestPartitions = await shard(requests, config.schedulingShardSize); - for (List requestPartition in requestPartitions) { - final BatchRequest batchRequest = BatchRequest(requests: requestPartition); - await pubsub.publish('scheduler-requests', batchRequest); - } - - return targets; - } - - /// Cancels all the current builds on [pullRequest] with [reason]. - /// - /// Builds are queried based on the [RepositorySlug] and pull request number. - Future cancelBuilds(github.PullRequest pullRequest, String reason) async { - log.info( - 'Attempting to cancel builds for pullrequest ${pullRequest.base!.repo!.fullName}/${pullRequest.number}', - ); - - final Iterable builds = await getTryBuildsByPullRequest(pullRequest); - log.info('Found ${builds.length} builds.'); - - if (builds.isEmpty) { - log.warning('No builds were found for pull request ${pullRequest.base!.repo!.fullName}.'); - return; - } - - final List requests = []; - for (Build build in builds) { - if (build.status == Status.scheduled || build.status == Status.started) { - // Scheduled status includes scheduled and pending tasks. - log.info('Cancelling build with build id ${build.id}.'); - requests.add( - Request( - cancelBuild: CancelBuildRequest( - id: build.id, - summaryMarkdown: reason, - ), - ), - ); - } - } - - if (requests.isNotEmpty) { - await buildBucketClient.batch(BatchRequest(requests: requests)); - } - } - - /// Filters [builders] to only those that failed on [pullRequest]. - Future> failedBuilds( - github.PullRequest pullRequest, - List targets, - ) async { - final Iterable builds = await getTryBuilds(pullRequest.base!.repo!.slug(), pullRequest.head!.sha!, null); - final Iterable builderNames = targets.map((Target target) => target.value.name); - // Return only builds that exist in the configuration file. - final Iterable failedBuilds = builds.where((Build? build) => failStatusSet.contains(build!.status)); - final Iterable expectedFailedBuilds = - failedBuilds.where((Build? build) => builderNames.contains(build!.builderId.builder)); - return expectedFailedBuilds.toList(); - } - - /// Sends [ScheduleBuildRequest] using information from a given build's - /// [BuildPushMessage]. - /// - /// The buildset, user_agent, and github_link tags are applied to match the - /// original build. The build properties and user data from the original build - /// are also preserved. - /// - /// The [currentAttempt] is used to track the number of current build attempt. - Future rescheduleBuild({ - required String builderName, - required push_message.BuildPushMessage buildPushMessage, - required int rescheduleAttempt, - }) async { - // Ensure we are using V2 bucket name istead of V1. - // V1 bucket name is "luci.flutter.prod" while the api - // is expecting just the last part after "."(prod). - final String bucketName = buildPushMessage.build!.bucket!.split('.').last; - final Map> tags = >{ - 'buildset': buildPushMessage.build!.tagsByName('buildset'), - 'user_agent': buildPushMessage.build!.tagsByName('user_agent'), - 'github_link': buildPushMessage.build!.tagsByName('github_link'), - 'cipd_version': buildPushMessage.build!.tagsByName('cipd_version'), - 'github_checkrun': buildPushMessage.build!.tagsByName('github_checkrun'), - 'current_attempt': [rescheduleAttempt.toString()], - }; - return buildBucketClient.scheduleBuild( - ScheduleBuildRequest( - builderId: BuilderId( - project: buildPushMessage.build!.project, - bucket: bucketName, - builder: builderName, - ), - tags: tags, - // We need to cast to to bypass json.encode error when scheduling builds. - properties: - (buildPushMessage.build!.buildParameters!['properties'] as Map).cast(), - notify: NotificationConfig( - pubsubTopic: 'projects/flutter-dashboard/topics/luci-builds', - userData: base64Encode(json.encode(buildPushMessage.userData).codeUnits), - ), - ), - ); - } - - /// Sends presubmit [ScheduleBuildRequest] for a pull request using [checkRunEvent]. - /// - /// Returns the [Build] returned by scheduleBuildRequest. - Future reschedulePresubmitBuildUsingCheckRunEvent(cocoon_checks.CheckRunEvent checkRunEvent) async { - final github.RepositorySlug slug = checkRunEvent.repository!.slug(); - - final String sha = checkRunEvent.checkRun!.headSha!; - final String checkName = checkRunEvent.checkRun!.name!; - - final github.CheckRun githubCheckRun = await githubChecksUtil.createCheckRun(config, slug, sha, checkName); - - final Iterable builds = await getTryBuilds(slug, sha, checkName); - if (builds.isEmpty) { - throw NoBuildFoundException('Unable to find try build.'); - } - - final Build build = builds.first; - final String prString = build.tags!['buildset']!.firstWhere((String? element) => element!.startsWith('pr/git/'))!; - final String cipdVersion = build.tags!['cipd_version']![0]!; - final String githubLink = build.tags!['github_link']![0]!; - final String repoName = githubLink.split('/')[4]; - final String branch = Config.defaultBranch(github.RepositorySlug('flutter', repoName)); - final int prNumber = int.parse(prString.split('/')[2]); - - final Map userData = { - 'check_run_id': githubCheckRun.id, - 'commit_branch': branch, - 'commit_sha': sha, - }; - final Map? properties = build.input!.properties; - log.info('input ${build.input!} properties $properties'); - - final ScheduleBuildRequest scheduleBuildRequest = _createPresubmitScheduleBuild( - slug: slug, - sha: sha, - checkName: checkName, - pullRequestNumber: prNumber, - cipdVersion: cipdVersion, - properties: properties, - userData: userData, - ); - - final Build scheduleBuild = await buildBucketClient.scheduleBuild(scheduleBuildRequest); - final String buildUrl = 'https://ci.chromium.org/ui/b/${scheduleBuild.id}'; - await githubChecksUtil.updateCheckRun(config, slug, githubCheckRun, detailsUrl: buildUrl); - return scheduleBuild; - } - - /// Sends postsubmit [ScheduleBuildRequest] for a commit using [checkRunEvent], [Commit], [Task], and [Target]. - /// - /// Returns the [Build] returned by scheduleBuildRequest. - Future reschedulePostsubmitBuildUsingCheckRunEvent( - cocoon_checks.CheckRunEvent checkRunEvent, { - required Commit commit, - required Task task, - required Target target, - }) async { - final github.RepositorySlug slug = checkRunEvent.repository!.slug(); - final String sha = checkRunEvent.checkRun!.headSha!; - final String checkName = checkRunEvent.checkRun!.name!; - - final Iterable builds = await getProdBuilds(slug, sha, checkName); - if (builds.isEmpty) { - throw NoBuildFoundException('Unable to find prod build.'); - } - - final Build build = builds.first; - final Map? properties = build.input!.properties; - log.info('input ${build.input!} properties $properties'); - - final ScheduleBuildRequest scheduleBuildRequest = - await _createPostsubmitScheduleBuild(commit: commit, target: target, task: task, properties: properties); - final Build scheduleBuild = await buildBucketClient.scheduleBuild(scheduleBuildRequest); - return scheduleBuild; - } - - /// Gets [Build] using its [id] and passing the additional - /// fields to be populated in the response. - Future getBuildById(String? id, {String? fields}) async { - final GetBuildRequest request = GetBuildRequest(id: id, fields: fields); - return buildBucketClient.getBuild(request); - } - - /// Gets builder list whose config is pre-defined in LUCI. - /// - /// Returns cache if existing. Otherwise make the RPC call to fetch list. - Future> getAvailableBuilderSet({ - String project = 'flutter', - String bucket = 'prod', - }) async { - final Uint8List? cacheValue = await cache.getOrCreate( - subCacheName, - 'builderlist', - createFn: () => _getAvailableBuilderSet(project: project, bucket: bucket), - // New commit triggering tasks should be finished within 5 mins. - // The batch backfiller's execution frequency is also 5 mins. - ttl: const Duration(minutes: 5), - ); - - return Set.from(String.fromCharCodes(cacheValue!).split(',')); - } - - /// Returns cache if existing, otherwise makes the RPC call to fetch list. - /// - /// Use [token] to make sure obtain all the list by calling RPC multiple times. - Future _getAvailableBuilderSet({ - String project = 'flutter', - String bucket = 'prod', - }) async { - log.info('No cached value for builderList, start fetching via the rpc call.'); - final Set availableBuilderSet = {}; - String? token; - do { - final ListBuildersResponse listBuildersResponse = await buildBucketClient.listBuilders( - ListBuildersRequest( - project: project, - bucket: bucket, - pageToken: token, - ), - ); - final List availableBuilderList = listBuildersResponse.builders!.map((e) => e.id!.builder!).toList(); - availableBuilderSet.addAll({...availableBuilderList}); - token = listBuildersResponse.nextPageToken; - } while (token != null); - final String joinedBuilderSet = availableBuilderSet.toList().join(','); - log.info('successfully fetched the builderSet: $joinedBuilderSet'); - return Uint8List.fromList(joinedBuilderSet.codeUnits); - } - - /// Schedules list of post-submit builds deferring work to [schedulePostsubmitBuild]. - /// - /// Returns empty list if all targets are successfully published to pub/sub. Otherwise, - /// returns the original list. - Future>> schedulePostsubmitBuilds({ - required Commit commit, - required List> toBeScheduled, - }) async { - if (toBeScheduled.isEmpty) { - log.fine('Skipping schedulePostsubmitBuilds as there are no targets to be scheduled by Cocoon'); - return toBeScheduled; - } - final List buildRequests = []; - Set availableBuilderSet; - try { - availableBuilderSet = await getAvailableBuilderSet(project: 'flutter', bucket: 'prod'); - } catch (error) { - log.severe('Failed to get buildbucket builder list due to $error'); - return toBeScheduled; - } - log.info('Available builder list: $availableBuilderSet'); - for (Tuple tuple in toBeScheduled) { - // Non-existing builder target will be skipped from scheduling. - if (!availableBuilderSet.contains(tuple.first.value.name)) { - log.warning('Found no available builder for ${tuple.first.value.name}, commit ${commit.sha}'); - continue; - } - log.info('create postsubmit schedule request for target: ${tuple.first.value} in commit ${commit.sha}'); - final ScheduleBuildRequest scheduleBuildRequest = await _createPostsubmitScheduleBuild( - commit: commit, - target: tuple.first, - task: tuple.second, - priority: tuple.third, - ); - buildRequests.add(Request(scheduleBuild: scheduleBuildRequest)); - log.info('created postsubmit schedule request for target: ${tuple.first.value} in commit ${commit.sha}'); - } - final BatchRequest batchRequest = BatchRequest(requests: buildRequests); - log.fine(batchRequest); - List messageIds; - try { - messageIds = await pubsub.publish('scheduler-requests', batchRequest); - log.info('Published $messageIds for commit ${commit.sha}'); - } catch (error) { - log.severe('Failed to publish message to pub/sub due to $error'); - return toBeScheduled; - } - log.info('Published a request with ${buildRequests.length} builds'); - return >[]; - } - - /// Create a Presubmit ScheduleBuildRequest using the [slug], [sha], and - /// [checkName] for the provided [build] with the provided [checkRunId]. - ScheduleBuildRequest _createPresubmitScheduleBuild({ - required github.RepositorySlug slug, - required String sha, - required String checkName, - required int pullRequestNumber, - required String cipdVersion, - Map? properties, - Map>? tags, - Map? userData, - List? dimensions, - }) { - final Map processedProperties = {}; - processedProperties.addAll(properties ?? {}); - processedProperties.addEntries( - { - 'git_url': 'https://github.com/${slug.owner}/${slug.name}', - 'git_ref': 'refs/pull/$pullRequestNumber/head', - 'exe_cipd_version': cipdVersion, - }.entries, - ); - - final Map processedUserData = userData ?? {}; - processedUserData['repo_owner'] = slug.owner; - processedUserData['repo_name'] = slug.name; - processedUserData['user_agent'] = 'flutter-cocoon'; - - final BuilderId builderId = BuilderId(project: 'flutter', bucket: 'try', builder: checkName); - - final Map> processedTags = tags ?? >{}; - processedTags['buildset'] = ['pr/git/$pullRequestNumber', 'sha/git/$sha']; - processedTags['user_agent'] = const ['flutter-cocoon']; - processedTags['github_link'] = ['https://github.com/${slug.owner}/${slug.name}/pull/$pullRequestNumber']; - processedTags['cipd_version'] = [cipdVersion]; - - final NotificationConfig notificationConfig = NotificationConfig( - pubsubTopic: 'projects/flutter-dashboard/topics/luci-builds', - userData: base64Encode(json.encode(processedUserData).codeUnits), - ); - - final Map exec = {'cipdVersion': cipdVersion}; - - return ScheduleBuildRequest( - builderId: builderId, - tags: processedTags, - properties: processedProperties, - notify: notificationConfig, - fields: 'id,builder,number,status,tags', - exe: exec, - dimensions: dimensions, - ); - } - - /// Creates a [ScheduleBuildRequest] for [target] and [task] against [commit]. - /// - /// By default, build [priority] is increased for release branches. - Future _createPostsubmitScheduleBuild({ - required Commit commit, - required Target target, - required Task task, - Map? properties, - Map>? tags, - int priority = kDefaultPriority, - }) async { - tags ??= >{}; - tags.addAll(>{ - 'buildset': [ - 'commit/git/${commit.sha}', - 'commit/gitiles/flutter.googlesource.com/mirrors/${commit.slug.name}/+/${commit.sha}', - ], - }); - - final String commitKey = task.parentKey!.id.toString(); - final String taskKey = task.key.id.toString(); - log.info('Scheduling builder: ${target.value.name} for commit ${commit.sha}'); - log.info('Task commit_key: $commitKey for task name: ${task.name}'); - log.info('Task task_key: $taskKey for task name: ${task.name}'); - - final Map rawUserData = { - 'commit_key': commitKey, - 'task_key': taskKey, - }; - - // Creates post submit checkrun only for unflaky targets from [config.postsubmitSupportedRepos]. - if (!target.value.bringup && config.postsubmitSupportedRepos.contains(target.slug)) { - await createPostsubmitCheckRun(commit, target, rawUserData); - } - - tags['user_agent'] = ['flutter-cocoon']; - // Tag `scheduler_job_id` is needed when calling buildbucket search build API. - tags['scheduler_job_id'] = ['flutter/${target.value.name}']; - final Map processedProperties = target.getProperties(); - processedProperties.addAll(properties ?? {}); - processedProperties['git_branch'] = commit.branch!; - final String cipdVersion = 'refs/heads/${commit.branch}'; - processedProperties['exe_cipd_version'] = cipdVersion; - return ScheduleBuildRequest( - builderId: BuilderId( - project: 'flutter', - bucket: target.getBucket(), - builder: target.value.name, - ), - dimensions: target.getDimensions(), - exe: { - 'cipdVersion': cipdVersion, - }, - gitilesCommit: GitilesCommit( - project: 'mirrors/${commit.slug.name}', - host: 'flutter.googlesource.com', - ref: 'refs/heads/${commit.branch}', - hash: commit.sha, - ), - notify: NotificationConfig( - pubsubTopic: 'projects/flutter-dashboard/topics/luci-builds-prod', - userData: base64Encode(json.encode(rawUserData).codeUnits), - ), - tags: tags, - properties: processedProperties, - priority: priority, - ); - } - - /// Creates postsubmit check runs for prod targets in supported repositories. - Future createPostsubmitCheckRun( - Commit commit, - Target target, - Map rawUserData, - ) async { - final github.CheckRun checkRun = await githubChecksUtil.createCheckRun( - config, - target.slug, - commit.sha!, - target.value.name, - ); - rawUserData['check_run_id'] = checkRun.id; - rawUserData['commit_sha'] = commit.sha; - rawUserData['commit_branch'] = commit.branch; - rawUserData['builder_name'] = target.value.name; - rawUserData['repo_owner'] = target.slug.owner; - rawUserData['repo_name'] = target.slug.name; - } - - /// Check to auto-rerun TOT test failures. - /// - /// A builder will be retried if: - /// 1. It has been tried below the max retry limit - /// 2. It is for the tip of tree - /// 3. The last known status is not green - /// 4. [ignoreChecks] is false. This allows manual reruns to bypass the Cocoon state. - Future checkRerunBuilder({ - required Commit commit, - required Target target, - required Task task, - required DatastoreService datastore, - Map>? tags, - bool ignoreChecks = false, - }) async { - if (ignoreChecks == false && await _shouldRerunBuilder(task, commit, datastore) == false) { - return false; - } - log.info('Rerun builder: ${target.value.name} for commit ${commit.sha}'); - tags ??= >{}; - tags['trigger_type'] = ['retry']; - - final BatchRequest request = BatchRequest( - requests: [ - Request( - scheduleBuild: await _createPostsubmitScheduleBuild( - commit: commit, - target: target, - task: task, - priority: kRerunPriority, - properties: Config.defaultProperties, - tags: tags, - ), - ), - ], - ); - await pubsub.publish('scheduler-requests', request); - - task.attempts = (task.attempts ?? 0) + 1; - // Mark task as in progress to ensure it isn't scheduled over - task.status = Task.statusInProgress; - await datastore.insert([task]); - - return true; - } - - /// Check if a builder should be rerun. - /// - /// A rerun happens when a build fails, the retry number hasn't reached the limit, and the build is on TOT. - Future _shouldRerunBuilder(Task task, Commit commit, DatastoreService? datastore) async { - if (!taskFailStatusSet.contains(task.status)) { - return false; - } - final int retries = task.attempts ?? 1; - if (retries > config.maxLuciTaskRetries) { - log.warning('Max retries reached'); - return false; - } - - final Commit latestCommit = await datastore! - .queryRecentCommits( - limit: 1, - slug: commit.slug, - branch: commit.branch, - ) - .single; - return latestCommit.sha == commit.sha; - } -} diff --git a/app_dart/lib/src/service/scheduler.dart b/app_dart/lib/src/service/scheduler.dart deleted file mode 100644 index fb33d6c84..000000000 --- a/app_dart/lib/src/service/scheduler.dart +++ /dev/null @@ -1,608 +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:math'; -import 'dart:typed_data'; - -import 'package:cocoon_service/src/service/exceptions.dart'; -import 'package:cocoon_service/src/service/build_status_provider.dart'; -import 'package:cocoon_service/src/service/scheduler/policy.dart'; -import 'package:gcloud/db.dart'; -import 'package:github/github.dart' as github; -import 'package:github/github.dart'; -import 'package:github/hooks.dart'; -import 'package:googleapis/bigquery/v2.dart'; -import 'package:retry/retry.dart'; -import 'package:truncate/truncate.dart'; -import 'package:yaml/yaml.dart'; - -import '../foundation/providers.dart'; -import '../foundation/typedefs.dart'; -import '../foundation/utils.dart'; -import '../model/appengine/commit.dart'; -import '../model/appengine/task.dart'; -import '../model/ci_yaml/ci_yaml.dart'; -import '../model/ci_yaml/target.dart'; -import '../model/github/checks.dart' as cocoon_checks; -import '../model/luci/buildbucket.dart'; -import '../model/proto/internal/scheduler.pb.dart' as pb; -import '../service/logging.dart'; -import 'cache_service.dart'; -import 'config.dart'; -import 'datastore.dart'; -import 'github_checks_service.dart'; -import 'github_service.dart'; -import 'luci_build_service.dart'; - -/// Scheduler service to validate all commits to supported Flutter repositories. -/// -/// Scheduler responsibilties include: -/// 1. Tracking commits in Cocoon -/// 2. Ensuring commits are validated (via scheduling tasks against commits) -/// 3. Retry mechanisms for tasks -class Scheduler { - Scheduler({ - required this.cache, - required this.config, - required this.githubChecksService, - required this.luciBuildService, - this.datastoreProvider = DatastoreService.defaultProvider, - this.httpClientProvider = Providers.freshHttpClient, - this.buildStatusProvider = BuildStatusService.defaultProvider, - }); - - final BuildStatusServiceProvider buildStatusProvider; - final CacheService cache; - final Config config; - final DatastoreServiceProvider datastoreProvider; - final GithubChecksService githubChecksService; - final HttpClientProvider httpClientProvider; - - late DatastoreService datastore; - LuciBuildService luciBuildService; - - /// Name of the subcache to store scheduler related values in redis. - static const String subcacheName = 'scheduler'; - - static const String kCiYamlCheckName = 'ci.yaml validation'; - - /// Ensure [commits] exist in Cocoon. - /// - /// If [Commit] does not exist in Datastore: - /// * Write it to datastore - /// * Schedule tasks listed in its scheduler config - /// Otherwise, ignore it. - Future addCommits(List commits) async { - datastore = datastoreProvider(config.db); - final List newCommits = await _getMissingCommits(commits); - log.fine('Found ${newCommits.length} new commits on GitHub'); - for (Commit commit in newCommits) { - await _addCommit(commit); - } - } - - /// Schedule tasks against [PullRequest]. - /// - /// If [PullRequest] was merged, schedule prod tasks against it. - /// Otherwise if it is presubmit, schedule try tasks against it. - Future addPullRequest(github.PullRequest pr) async { - datastore = datastoreProvider(config.db); - // TODO(chillers): Support triggering on presubmit. https://github.com/flutter/flutter/issues/77858 - if (!pr.merged!) { - log.warning('Only pull requests that were closed and merged should have tasks scheduled'); - return; - } - - final String fullRepo = pr.base!.repo!.fullName; - final String? branch = pr.base!.ref; - final String sha = pr.mergeCommitSha!; - - final String id = '$fullRepo/$branch/$sha'; - final Key key = datastore.db.emptyKey.append(Commit, id: id); - final Commit mergedCommit = Commit( - author: pr.user!.login!, - authorAvatarUrl: pr.user!.avatarUrl!, - branch: branch, - key: key, - // The field has a max length of 1500 so ensure the commit message is not longer. - message: truncate(pr.title!, 1490, omission: '...'), - repository: fullRepo, - sha: sha, - timestamp: pr.mergedAt!.millisecondsSinceEpoch, - ); - - if (await _commitExistsInDatastore(mergedCommit)) { - log.fine('$sha already exists in datastore. Scheduling skipped.'); - return; - } - - log.fine('Scheduling $sha via GitHub webhook'); - await _addCommit(mergedCommit); - } - - /// Processes postsubmit tasks. - Future _addCommit(Commit commit) async { - if (!config.supportedRepos.contains(commit.slug)) { - log.fine('Skipping ${commit.id} as repo is not supported'); - return; - } - - final CiYaml ciYaml = await getCiYaml(commit); - - final List initialTargets = ciYaml.getInitialTargets(ciYaml.postsubmitTargets); - final List tasks = targetsToTask(commit, initialTargets).toList(); - - final List> toBeScheduled = >[]; - for (Target target in initialTargets) { - final Task task = tasks.singleWhere((Task task) => task.name == target.value.name); - SchedulerPolicy policy = target.schedulerPolicy; - // Release branches should run every task - if (Config.defaultBranch(commit.slug) != commit.branch) { - policy = GuaranteedPolicy(); - } - final int? priority = await policy.triggerPriority(task: task, datastore: datastore); - if (priority != null) { - // Mark task as in progress to ensure it isn't scheduled over - task.status = Task.statusInProgress; - toBeScheduled.add(Tuple(target, task, priority)); - } - } - - // Datastore must be written to generate task keys - try { - await datastore.withTransaction((Transaction transaction) async { - transaction.queueMutations(inserts: [commit]); - transaction.queueMutations(inserts: tasks); - await transaction.commit(); - log.fine('Committed ${tasks.length} new tasks for commit ${commit.sha!}'); - }); - } catch (error) { - log.severe('Failed to add commit ${commit.sha!}: $error'); - } - - await _batchScheduleBuilds(commit, toBeScheduled); - await _uploadToBigQuery(commit); - } - - /// Schedule all builds in batch requests instead of a single request. - /// - /// Each batch request contains [Config.batchSize] builds to be scheduled. - Future _batchScheduleBuilds(Commit commit, List> toBeScheduled) async { - log.info('Batching ${toBeScheduled.length} for ${commit.sha}'); - final List> futures = >[]; - for (int i = 0; i < toBeScheduled.length; i += config.batchSize) { - futures.add( - luciBuildService.schedulePostsubmitBuilds( - commit: commit, - toBeScheduled: toBeScheduled.sublist(i, min(i + config.batchSize, toBeScheduled.length)), - ), - ); - } - await Future.wait(futures); - } - - /// Return subset of [commits] not stored in Datastore. - Future> _getMissingCommits(List commits) async { - final List newCommits = []; - // Ensure commits are sorted from newest to oldest (descending order) - commits.sort((Commit a, Commit b) => b.timestamp!.compareTo(a.timestamp!)); - for (Commit commit in commits) { - // Cocoon may randomly drop commits, so check the entire list. - if (!await _commitExistsInDatastore(commit)) { - newCommits.add(commit); - } - } - - // Reverses commits to be in order of oldest to newest. - return newCommits; - } - - /// Whether [Commit] already exists in [datastore]. - /// - /// Datastore is Cocoon's source of truth for what commits have been scheduled. - /// Since webhooks or cron jobs can schedule commits, we must verify a commit - /// has not already been scheduled. - Future _commitExistsInDatastore(Commit commit) async { - try { - await datastore.db.lookupValue(commit.key); - } on KeyNotFoundException { - return false; - } - return true; - } - - /// Process and filters ciyaml. - Future getCiYaml( - Commit commit, { - bool validate = false, - }) async { - final Commit totCommit = await generateTotCommit(slug: commit.slug, branch: Config.defaultBranch(commit.slug)); - final CiYaml totYaml = await _getCiYaml(totCommit); - return _getCiYaml(commit, totCiYaml: totYaml, validate: validate); - } - - /// Load in memory the `.ci.yaml`. - Future _getCiYaml( - Commit commit, { - CiYaml? totCiYaml, - bool validate = false, - RetryOptions retryOptions = const RetryOptions(delayFactor: Duration(seconds: 2), maxAttempts: 4), - }) async { - String ciPath; - ciPath = '${commit.repository}/${commit.sha!}/$kCiYamlPath'; - final Uint8List ciYamlBytes = (await cache.getOrCreate( - subcacheName, - ciPath, - createFn: () => _downloadCiYaml( - commit, - ciPath, - retryOptions: retryOptions, - ), - ttl: const Duration(hours: 1), - ))!; - final pb.SchedulerConfig schedulerConfig = pb.SchedulerConfig.fromBuffer(ciYamlBytes); - log.fine('Retrieved .ci.yaml for $ciPath'); - // If totCiYaml is not null, we assume upper level function has verified that current branch is not a release branch. - return CiYaml( - config: schedulerConfig, - slug: commit.slug, - branch: commit.branch!, - totConfig: totCiYaml, - validate: validate, - ); - } - - /// Get `.ci.yaml` from GitHub, and store the bytes in redis for future retrieval. - /// - /// If GitHub returns [HttpStatus.notFound], an empty config will be inserted assuming - /// that commit does not support the scheduler config file. - Future _downloadCiYaml( - Commit commit, - String ciPath, { - RetryOptions retryOptions = const RetryOptions(maxAttempts: 3), - }) async { - final String configContent = await githubFileContent( - commit.slug, - '.ci.yaml', - httpClientProvider: httpClientProvider, - ref: commit.sha!, - retryOptions: retryOptions, - ); - final YamlMap configYaml = loadYaml(configContent) as YamlMap; - final pb.SchedulerConfig schedulerConfig = pb.SchedulerConfig()..mergeFromProto3Json(configYaml); - return schedulerConfig.writeToBuffer(); - } - - /// Cancel all incomplete targets against a pull request. - Future cancelPreSubmitTargets({ - required github.PullRequest pullRequest, - String reason = 'Newer commit available', - }) async { - await luciBuildService.cancelBuilds(pullRequest, reason); - } - - /// Schedule presubmit targets against a pull request. - /// - /// Cancels all existing targets then schedules the targets. - /// - /// Schedules a [kCiYamlCheckName] to validate [CiYaml] is valid and all builds were able to be triggered. - /// If [builderTriggerList] is specified, then trigger only those targets. - Future triggerPresubmitTargets({ - required github.PullRequest pullRequest, - String reason = 'Newer commit available', - List? builderTriggerList, - }) async { - // Always cancel running builds so we don't ever schedule duplicates. - log.info('Attempting to cancel existing presubmit targets for ${pullRequest.number}'); - await cancelPreSubmitTargets( - pullRequest: pullRequest, - reason: reason, - ); - - log.info('Creating ciYaml validation check run for ${pullRequest.number}'); - final github.CheckRun ciValidationCheckRun = await githubChecksService.githubChecksUtil.createCheckRun( - config, - pullRequest.base!.repo!.slug(), - pullRequest.head!.sha!, - kCiYamlCheckName, - output: const github.CheckRunOutput( - title: kCiYamlCheckName, - summary: 'If this check is stuck pending, push an empty commit to retrigger the checks', - ), - ); - - log.info('Creating presubmit targets for ${pullRequest.number}'); - final github.RepositorySlug slug = pullRequest.base!.repo!.slug(); - dynamic exception; - try { - // Both the author and label should be checked to make sure that no one is - // attempting to get a pull request without check through. - if (pullRequest.user!.login == config.autosubmitBot && - pullRequest.labels!.any((element) => element.name == Config.revertOfLabel)) { - log.info('Skipping generating the full set of checks for revert request.'); - } else { - final List presubmitTargets = await getPresubmitTargets(pullRequest); - final List presubmitTriggerTargets = getTriggerList(presubmitTargets, builderTriggerList); - await luciBuildService.scheduleTryBuilds( - targets: presubmitTriggerTargets, - pullRequest: pullRequest, - ); - } - } on FormatException catch (error, backtrace) { - log.warning('FormatException encountered when scheduling presubmit targets for ${pullRequest.number}'); - log.warning(backtrace.toString()); - exception = error; - } catch (error, backtrace) { - log.warning('Exception encountered when scheduling presubmit targets for ${pullRequest.number}'); - log.warning(backtrace.toString()); - exception = error; - } - - // Update validate ci.yaml check - log.info('Updating ci.yaml validation check for ${pullRequest.number}'); - if (exception == null) { - // Success in validating ci.yaml - log.info('ci.yaml validation check was successful for ${pullRequest.number}'); - await githubChecksService.githubChecksUtil.updateCheckRun( - config, - slug, - ciValidationCheckRun, - status: github.CheckRunStatus.completed, - conclusion: github.CheckRunConclusion.success, - ); - } else { - log.warning('Marking PR #${pullRequest.number} $kCiYamlCheckName as failed'); - log.warning(exception.toString()); - // Failure when validating ci.yaml - await githubChecksService.githubChecksUtil.updateCheckRun( - config, - slug, - ciValidationCheckRun, - status: github.CheckRunStatus.completed, - conclusion: github.CheckRunConclusion.failure, - output: github.CheckRunOutput( - title: kCiYamlCheckName, - summary: '.ci.yaml has failures', - text: exception.toString(), - ), - ); - } - log.info( - 'Finished triggering builds for: pr ${pullRequest.number}, commit ${pullRequest.base!.sha}, branch ${pullRequest.head!.ref} and slug ${pullRequest.base!.repo!.slug()}}', - ); - } - - /// If [builderTriggerList] is specificed, return only builders that are contained in [presubmitTarget]. - /// Otherwise, return [presubmitTarget]. - List getTriggerList(List presubmitTarget, List? builderTriggerList) { - if (builderTriggerList != null && builderTriggerList.isNotEmpty) { - return presubmitTarget.where((Target target) => builderTriggerList.contains(target.value.name)).toList(); - } - return presubmitTarget; - } - - /// Given a pull request event, retry all failed LUCI checks. - /// - /// 1. Aggregate .ci.yaml and try_builders.json presubmit builds. - /// 2. Get failed LUCI builds for this pull request at [commitSha]. - /// 3. Rerun the failed builds that also have a failed check status. - Future retryPresubmitTargets({ - required github.PullRequest pullRequest, - required CheckSuiteEvent checkSuiteEvent, - }) async { - final github.GitHub githubClient = await config.createGitHubClient(pullRequest: pullRequest); - final Map checkRuns = await githubChecksService.githubChecksUtil.allCheckRuns( - githubClient, - checkSuiteEvent, - ); - final List presubmitTargets = await getPresubmitTargets(pullRequest); - final List failedBuilds = await luciBuildService.failedBuilds(pullRequest, presubmitTargets); - for (Build? build in failedBuilds) { - final github.CheckRun checkRun = checkRuns[build!.builderId.builder!]!; - - if (checkRun.status != github.CheckRunStatus.completed) { - // Check run is still in progress, do not retry. - continue; - } - - await luciBuildService.scheduleTryBuilds( - targets: presubmitTargets.where((Target target) => build.builderId.builder == target.value.name).toList(), - pullRequest: pullRequest, - checkSuiteEvent: checkSuiteEvent, - ); - } - } - - /// Get LUCI presubmit builders from .ci.yaml. - /// - /// Filters targets with runIf, matching them to the diff of [pullRequest]. - /// - /// In the case there is an issue getting the diff from GitHub, all targets are returned. - Future> getPresubmitTargets(github.PullRequest pullRequest) async { - final Commit commit = Commit( - branch: pullRequest.base!.ref, - repository: pullRequest.base!.repo!.fullName, - sha: pullRequest.head!.sha, - ); - late CiYaml ciYaml; - log.info('Attempting to read presubmit targets from ci.yaml for ${pullRequest.number}'); - if (commit.branch == Config.defaultBranch(commit.slug)) { - ciYaml = await getCiYaml(commit, validate: true); - } else { - ciYaml = await getCiYaml(commit); - } - log.info('ci.yaml loaded successfully.'); - log.info('Collecting presubmit targets for ${pullRequest.number}'); - - // Filter out schedulers targets with schedulers different than luci or cocoon. - final Iterable presubmitTargets = ciYaml.presubmitTargets.where( - (Target target) => - target.value.scheduler == pb.SchedulerSystem.luci || target.value.scheduler == pb.SchedulerSystem.cocoon, - ); - - log.info('Collected ${presubmitTargets.length} presubmit targets.'); - // Release branches should run every test. - if (pullRequest.base!.ref != Config.defaultBranch(pullRequest.base!.repo!.slug())) { - log.info('Release branch found, scheduling all targets for ${pullRequest.number}'); - return presubmitTargets.toList(); - } - - // Filter builders based on the PR diff - final GithubService githubService = await config.createGithubService(commit.slug); - List files = []; - try { - files = await githubService.listFiles(pullRequest); - } on github.GitHubError catch (error) { - log.warning(error); - log.warning('Unable to get diff for pullRequest=$pullRequest'); - log.warning('Running all targets'); - return presubmitTargets.toList(); - } - return getTargetsToRun(presubmitTargets, files); - } - - /// Reschedules a failed build using a [CheckRunEvent]. The CheckRunEvent is - /// generated when someone clicks the re-run button from a failed build from - /// the Github UI. - /// - /// If the rerequested check is for [kCiYamlCheckName], all presubmit jobs are retried. - /// Otherwise, the specific check will be retried. - /// - /// Relevant APIs: - /// https://developer.github.com/v3/checks/runs/#check-runs-and-requested-actions - Future processCheckRun(cocoon_checks.CheckRunEvent checkRunEvent) async { - switch (checkRunEvent.action) { - case 'rerequested': - log.fine('Rerun requested by GitHub user: ${checkRunEvent.sender?.login}'); - final String? name = checkRunEvent.checkRun!.name; - bool success = false; - if (name == kCiYamlCheckName) { - // The CheckRunEvent.checkRun.pullRequests array is empty for this - // event, so we need to find the matching pull request. - final RepositorySlug slug = checkRunEvent.repository!.slug(); - final String headSha = checkRunEvent.checkRun!.headSha!; - final int checkSuiteId = checkRunEvent.checkRun!.checkSuite!.id!; - final PullRequest? pullRequest = - await githubChecksService.findMatchingPullRequest(slug, headSha, checkSuiteId); - if (pullRequest != null) { - log.fine('Matched PR: ${pullRequest.number} Repo: ${slug.fullName}'); - await triggerPresubmitTargets(pullRequest: pullRequest); - success = true; - } else { - log.warning('No matching PR found for head_sha in check run event.'); - } - } else { - try { - final RepositorySlug slug = checkRunEvent.repository!.slug(); - final String gitBranch = checkRunEvent.checkRun!.checkSuite!.headBranch ?? Config.defaultBranch(slug); - final String sha = checkRunEvent.checkRun!.headSha!; - - // Only merged commits are added to the datastore. If a matching commit is found, this must be a postsubmit checkrun. - datastore = datastoreProvider(config.db); - final Key commitKey = - Commit.createKey(db: datastore.db, slug: slug, gitBranch: gitBranch, sha: sha); - Commit? commit; - try { - commit = await Commit.fromDatastore(datastore: datastore, key: commitKey); - log.fine('Commit found in datastore.'); - } on KeyNotFoundException { - log.fine('Commit not found in datastore.'); - } - - if (commit == null) { - log.fine('Rescheduling presubmit build.'); - await luciBuildService.reschedulePresubmitBuildUsingCheckRunEvent(checkRunEvent); - } else { - log.fine('Rescheduling postsubmit build.'); - final String checkName = checkRunEvent.checkRun!.name!; - final Task task = await Task.fromDatastore(datastore: datastore, commitKey: commitKey, name: checkName); - final CiYaml ciYaml = await getCiYaml(commit); - final Target target = - ciYaml.postsubmitTargets.singleWhere((Target target) => target.value.name == task.name); - await luciBuildService.reschedulePostsubmitBuildUsingCheckRunEvent( - checkRunEvent, - commit: commit, - task: task, - target: target, - ); - } - - success = true; - } on NoBuildFoundException { - log.warning('No build found to reschedule.'); - } - } - - log.fine('CheckName: $name State: $success'); - return success; - } - - return true; - } - - /// Push [Commit] to BigQuery as part of the infra metrics dashboards. - Future _uploadToBigQuery(Commit commit) async { - const String projectId = 'flutter-dashboard'; - const String dataset = 'cocoon'; - const String table = 'Checklist'; - - log.info('Uploading commit ${commit.sha} info to bigquery.'); - - final TabledataResource tabledataResource = await config.createTabledataResourceApi(); - final List> tableDataInsertAllRequestRows = >[]; - - /// Consolidate [commits] together - /// - /// Prepare for bigquery [insertAll] - tableDataInsertAllRequestRows.add({ - 'json': { - 'ID': commit.id, - 'CreateTimestamp': commit.timestamp, - 'FlutterRepositoryPath': commit.repository, - 'CommitSha': commit.sha!, - 'CommitAuthorLogin': commit.author, - 'CommitAuthorAvatarURL': commit.authorAvatarUrl, - 'CommitMessage': commit.message, - 'Branch': commit.branch, - }, - }); - - /// Final [rows] to be inserted to [BigQuery] - final TableDataInsertAllRequest rows = - TableDataInsertAllRequest.fromJson({'rows': tableDataInsertAllRequestRows}); - - /// Insert [commits] to [BigQuery] - try { - if (rows.rows == null) { - log.warning('Rows to be inserted is null'); - } else { - log.info('Inserting ${rows.rows!.length} into big query for ${commit.sha}'); - } - await tabledataResource.insertAll(rows, projectId, dataset, table); - } on ApiRequestError { - log.warning('Failed to add commits to BigQuery: $ApiRequestError'); - } - } - - /// Returns the tip of tree [Commit] using specified [branch] and [RepositorySlug]. - /// - /// A tip of tree [Commit] is used to help generate the tip of tree [CiYaml]. - /// The generated tip of tree [CiYaml] will be compared against Presubmit Targets in current [CiYaml], - /// to ensure new targets without `bringup: true` label are not added into the build. - Future generateTotCommit({required String branch, required RepositorySlug slug}) async { - datastore = datastoreProvider(config.db); - final BuildStatusService buildStatusService = buildStatusProvider(datastore); - final Commit totCommit = (await buildStatusService - .retrieveCommitStatus( - limit: 1, - branch: branch, - slug: slug, - ) - .map((CommitStatus status) => status.commit) - .toList()) - .single; - - return totCommit; - } -} diff --git a/app_dart/lib/src/service/scheduler/policy.dart b/app_dart/lib/src/service/scheduler/policy.dart deleted file mode 100644 index 62f35389e..000000000 --- a/app_dart/lib/src/service/scheduler/policy.dart +++ /dev/null @@ -1,119 +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:cocoon_service/src/service/datastore.dart'; - -import '../../model/appengine/task.dart'; -import '../logging.dart'; -import '../luci_build_service.dart'; - -/// Interface for implementing various scheduling policies in the Cocoon scheduler. -abstract class SchedulerPolicy { - /// Returns the priority of [Task]. - /// - /// If null is returned, the task should not be scheduled. - Future triggerPriority({ - required Task task, - required DatastoreService datastore, - }); -} - -/// Every [Task] is triggered to run. -class GuaranteedPolicy implements SchedulerPolicy { - @override - Future triggerPriority({ - required Task task, - required DatastoreService datastore, - }) async { - final List recentTasks = await datastore.queryRecentTasksByName(name: task.name!).toList(); - // Ensure task isn't considered in recentTasks - recentTasks.removeWhere((Task t) => t.commitKey == task.commitKey); - if (recentTasks.isEmpty) { - log.warning('${task.name} is newly added, triggerring builds regardless of policy'); - return LuciBuildService.kDefaultPriority; - } - // Prioritize tasks that recently failed. - if (shouldRerunPriority(recentTasks, 1)) { - return LuciBuildService.kRerunPriority; - } - return LuciBuildService.kDefaultPriority; - } -} - -/// [Task] is run at least every 6 commits. -/// -/// If there is capacity, a backfiller cron triggers the latest task that was not run -/// to ensure ToT is always tested. -/// -/// This is intended for targets that are run in an infra pool that has limited capacity, -/// such as the on device tests in the DeviceLab. -class BatchPolicy implements SchedulerPolicy { - static const int kBatchSize = 6; - @override - Future triggerPriority({ - required Task task, - required DatastoreService datastore, - }) async { - final List recentTasks = await datastore.queryRecentTasksByName(name: task.name!).toList(); - // Skip scheduling if there is already a running task. - if (recentTasks.any((Task task) => task.status == Task.statusInProgress)) { - return null; - } - - // Ensure task isn't considered in recentTasks - recentTasks.removeWhere((Task t) => t.commitKey == task.commitKey); - if (recentTasks.length < kBatchSize) { - log.warning('${task.name} has less than $kBatchSize, skip scheduling to wait for ci.yaml roll.'); - return null; - } - - // Prioritize tasks that recently failed. - if (shouldRerunPriority(recentTasks, kBatchSize)) { - return LuciBuildService.kRerunPriority; - } - - if (allNew(recentTasks.sublist(0, kBatchSize - 1))) { - return LuciBuildService.kDefaultPriority; - } - - return null; - } -} - -/// Checks if all tasks are with [Task.statusNew]. -bool allNew(List tasks) { - for (Task task in tasks) { - if (task.status != Task.statusNew) { - return false; - } - } - return true; -} - -/// Return true if there is an earlier failed build. -bool shouldRerunPriority(List tasks, int pastTaskNumber) { - // Prioritize tasks that recently failed. - bool hasRecentFailure = false; - for (int i = 0; i < pastTaskNumber && i < tasks.length; i++) { - if (_isFailed(tasks[i])) { - hasRecentFailure = true; - break; - } - } - return hasRecentFailure; -} - -bool _isFailed(Task task) { - return task.status == Task.statusFailed || task.status == Task.statusInfraFailure; -} - -/// [Task] run outside of Cocoon are not triggered by the Cocoon scheduler. -class OmitPolicy implements SchedulerPolicy { - @override - Future triggerPriority({ - required Task task, - required DatastoreService datastore, - }) async => - null; -} diff --git a/app_dart/pubspec.yaml b/app_dart/pubspec.yaml deleted file mode 100644 index c6f9fc746..000000000 --- a/app_dart/pubspec.yaml +++ /dev/null @@ -1,55 +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. - -name: cocoon_service -description: AppEngine service for managing Flutter CI -homepage: https://github.com/flutter/cocoon -publish_to: none - -environment: - sdk: '>=3.0.0-0 <4.0.0' - -dependencies: - appengine: 0.13.7 - args: 2.4.2 - collection: 1.18.0 - corsac_jwt: 1.0.0-nullsafety.1 - crypto: 3.0.3 - dbcrypt: 2.0.0 - file: 7.0.0 - fixnum: 1.1.0 - gcloud: 0.8.11 - github: 9.19.0 - googleapis: 11.4.0 - googleapis_auth: 1.4.1 - gql: 1.0.1-alpha+1696717343881 - graphql: 5.2.0-beta.6 - grpc: 3.2.4 - http: 1.1.0 - json_annotation: 4.8.1 - logging: 1.2.0 - meta: 1.11.0 - mime: 1.0.4 - mutex: 3.1.0 - neat_cache: 2.0.3 - path: 1.8.3 - process: 5.0.1 - process_runner: 4.1.4 - protobuf: 2.1.0 - retry: ^3.1.2 - truncate: 3.0.1 - yaml: 3.1.2 - -dev_dependencies: - analyzer: 5.13.0 - build_runner: 2.4.6 - fake_async: 1.3.1 - flutter_lints: 3.0.1 - json_serializable: 6.7.1 - mockito: 5.4.2 - platform: 3.1.3 - test: 1.24.9 - -builders: - json_serializable: 3.3.0 diff --git a/app_dart/test/foundation/utils_test.dart b/app_dart/test/foundation/utils_test.dart deleted file mode 100644 index 9af49b42b..000000000 --- a/app_dart/test/foundation/utils_test.dart +++ /dev/null @@ -1,392 +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:io'; - -import 'package:cocoon_service/src/foundation/utils.dart'; -import 'package:cocoon_service/src/model/ci_yaml/target.dart'; -import 'package:cocoon_service/src/service/logging.dart'; -import 'package:github/github.dart'; -import 'package:googleapis/bigquery/v2.dart'; -import 'package:http/http.dart' as http; -import 'package:http/testing.dart'; -import 'package:logging/logging.dart'; -import 'package:retry/retry.dart'; -import 'package:test/test.dart'; - -import '../src/bigquery/fake_tabledata_resource.dart'; -import '../src/utilities/entity_generators.dart'; - -const String branchRegExp = ''' - master - flutter-1.1-candidate.1 - '''; -const String luciBuilders = ''' - { - "builders":[ - { - "name":"Cocoon", - "repo":"cocoon", - "enabled":true - }, { - "name":"Cocoon2", - "repo":"cocoon", - "enabled":false - } - ] - } - '''; - -void main() { - group('Test utils', () { - const RetryOptions noRetry = RetryOptions( - maxAttempts: 1, - delayFactor: Duration.zero, - maxDelay: Duration.zero, - ); - group('githubFileContent', () { - late MockClient branchHttpClient; - - test('returns branches', () async { - branchHttpClient = MockClient((_) async => http.Response(branchRegExp, HttpStatus.ok)); - final String branches = await githubFileContent( - RepositorySlug('flutter', 'cocoon'), - 'branches.txt', - httpClientProvider: () => branchHttpClient, - retryOptions: noRetry, - ); - final List branchList = branches.split('\n').map((String branch) => branch.trim()).toList(); - branchList.removeWhere((String branch) => branch.isEmpty); - expect(branchList, ['master', 'flutter-1.1-candidate.1']); - }); - - test('retries branches download upon HTTP failure', () async { - int retry = 0; - branchHttpClient = MockClient((_) async { - if (retry++ == 0) { - return http.Response('', HttpStatus.serviceUnavailable); - } - return http.Response(branchRegExp, HttpStatus.ok); - }); - final List records = []; - log.onRecord.listen((LogRecord record) => records.add(record)); - final String branches = await githubFileContent( - RepositorySlug('flutter', 'cocoon'), - 'branches.txt', - httpClientProvider: () => branchHttpClient, - retryOptions: const RetryOptions( - maxAttempts: 3, - delayFactor: Duration.zero, - maxDelay: Duration.zero, - ), - ); - final List branchList = branches.split('\n').map((String branch) => branch.trim()).toList(); - branchList.removeWhere((String branch) => branch.isEmpty); - expect(retry, 2); - expect(branchList, ['master', 'flutter-1.1-candidate.1']); - expect(records.where((LogRecord record) => record.level == Level.INFO), isNotEmpty); - expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty); - }); - - test('falls back to git on borg', () async { - branchHttpClient = MockClient((http.Request request) async { - if (request.url.toString() == - 'https://flutter.googlesource.com/mirrors/cocoon/+/ba7fe03781762603a1cdc364f8f5de56a0fdbf5c/.ci.yaml?format=text') { - return http.Response(base64Encode(branchRegExp.codeUnits), HttpStatus.ok); - } - // Mock a GitHub outage - return http.Response('', HttpStatus.serviceUnavailable); - }); - final List records = []; - log.onRecord.listen((LogRecord record) => records.add(record)); - final String branches = await githubFileContent( - RepositorySlug('flutter', 'cocoon'), - '.ci.yaml', - httpClientProvider: () => branchHttpClient, - ref: 'ba7fe03781762603a1cdc364f8f5de56a0fdbf5c', - retryOptions: const RetryOptions( - maxAttempts: 1, - delayFactor: Duration.zero, - maxDelay: Duration.zero, - ), - ); - final List branchList = branches.split('\n').map((String branch) => branch.trim()).toList(); - branchList.removeWhere((String branch) => branch.isEmpty); - expect(branchList, ['master', 'flutter-1.1-candidate.1']); - }); - - test('falls back to git on borg when given sha', () async { - branchHttpClient = MockClient((http.Request request) async { - if (request.url.toString() == - 'https://flutter.googlesource.com/mirrors/cocoon/+/refs/heads/main/.ci.yaml?format=text') { - return http.Response(base64Encode(branchRegExp.codeUnits), HttpStatus.ok); - } - // Mock a GitHub outage - return http.Response('', HttpStatus.serviceUnavailable); - }); - final List records = []; - log.onRecord.listen((LogRecord record) => records.add(record)); - final String branches = await githubFileContent( - RepositorySlug('flutter', 'cocoon'), - '.ci.yaml', - ref: 'main', - httpClientProvider: () => branchHttpClient, - retryOptions: const RetryOptions( - maxAttempts: 1, - delayFactor: Duration.zero, - maxDelay: Duration.zero, - ), - ); - final List branchList = branches.split('\n').map((String branch) => branch.trim()).toList(); - branchList.removeWhere((String branch) => branch.isEmpty); - expect(branchList, ['master', 'flutter-1.1-candidate.1']); - }); - - test('gives up after 6 tries', () async { - int retry = 0; - branchHttpClient = MockClient((_) async { - retry++; - return http.Response('', HttpStatus.serviceUnavailable); - }); - final List records = []; - log.onRecord.listen((LogRecord record) => records.add(record)); - await expectLater( - githubFileContent( - RepositorySlug('flutter', 'cocoon'), - 'branches.txt', - httpClientProvider: () => branchHttpClient, - retryOptions: const RetryOptions( - maxAttempts: 3, - delayFactor: Duration.zero, - maxDelay: Duration.zero, - ), - ), - throwsA(isA()), - ); - // It will request from GitHub 3 times, fallback to GoB, then fail. - expect(retry, 6); - expect(records.where((LogRecord record) => record.level == Level.WARNING), isNotEmpty); - }); - }); - - group('GitHubBackoffCalculator', () { - test('twoSecondLinearBackoff', () { - expect(twoSecondLinearBackoff(0), const Duration(seconds: 2)); - expect(twoSecondLinearBackoff(1), const Duration(seconds: 4)); - expect(twoSecondLinearBackoff(2), const Duration(seconds: 6)); - expect(twoSecondLinearBackoff(3), const Duration(seconds: 8)); - }); - }); - - group('bigquery', () { - late FakeTabledataResource tabledataResourceApi; - - setUp(() { - tabledataResourceApi = FakeTabledataResource(); - }); - test('Insert data to bigquery', () async { - await insertBigquery('test', {'test': 'test'}, tabledataResourceApi); - final TableDataList tableDataList = await tabledataResourceApi.list('test', 'test', 'test'); - expect(tableDataList.totalRows, '1'); - }); - }); - - group('getFilteredBuilders', () { - test('does not return builders when run_if does not match any file', () async { - final List targets = [ - generateTarget(1, runIf: ['cde/']), - ]; - final List files = ['abc/cde.py', 'cde/fgh.dart']; - final List result = await getTargetsToRun(targets, files); - expect(result.isEmpty, isTrue); - }); - - test('returns builders when run_if is null', () async { - final List files = ['abc/def.py', 'cde/dgh.dart']; - final List targets = [generateTarget(1)]; - final List result = await getTargetsToRun(targets, files); - expect(result, targets); - }); - - test('returns builders when run_if matches files using full path', () async { - final List files = ['abc/cde.py', 'cgh/dhj.dart']; - final List targets = [ - generateTarget(1, runIf: ['abc/cde.py']), - ]; - final List result = await getTargetsToRun(targets, files); - expect(result, targets); - }); - - test('returns builders when run_if matches files with **', () async { - final List targets = [ - generateTarget(1, runIf: ['abc/**']), - ]; - final List files = ['abc/cdf/hj.dart', 'abc/dej.dart']; - final List result = await getTargetsToRun(targets, files); - expect(result, targets); - }); - - test('returns builders when run_if matches files with ** that contain digits', () async { - final List targets = [ - generateTarget( - 1, - runIf: [ - 'dev/**', - 'packages/flutter/**', - 'packages/flutter_driver/**', - 'packages/integration_test/**', - 'packages/flutter_localizations/**', - 'packages/fuchsia_remote_debug_protocol/**', - 'packages/flutter_test/**', - 'packages/flutter_goldens/**', - 'packages/flutter_tools/**', - 'bin/**', - '.ci.yaml', - ], - ), - ]; - final List files = [ - 'packages/flutter_localizations/lib/src/l10n/material_es.arb', - 'packages/flutter_localizations/lib/src/l10n/material_en_ZA.arb', - ]; - final List result = await getTargetsToRun(targets, files); - expect(result, targets); - }); - - test('returns builders when run_if matches files with * and ** that contains digits', () async { - final List targets = [ - generateTarget( - 1, - runIf: [ - 'dev/**', - 'packages/flutter/**', - 'packages/flutter_driver/**', - 'packages/integration_test/**', - 'packages/flutter_localizations/**/l10n/cupertino*.arb', - 'packages/fuchsia_remote_debug_protocol/**', - 'packages/flutter_test/**', - 'packages/flutter_goldens/**', - 'packages/flutter_tools/**', - 'bin/**', - '.ci.yaml', - ], - ), - ]; - final List files = [ - 'packages/flutter_localizations/lib/src/l10n/material_es.arb', - 'packages/flutter_localizations/lib/src/l10n/material_en_ZA.arb', - 'packages/flutter_localizations/lib/src/l10n/cupertino_cy.arb', - ]; - final List result = await getTargetsToRun(targets, files); - expect(result, targets); - }); - - test('returns builders when run_if matches files with * trailing glob', () async { - final List targets = [ - generateTarget( - 1, - runIf: [ - 'packages/flutter_localizations/**/l10n/*', - ], - ), - ]; - final List files = [ - 'packages/flutter_localizations/lib/src/l10n/material_es.arb', - 'packages/flutter_localizations/lib/src/l10n/material_en_ZA.arb', - 'packages/flutter_localizations/lib/src/l10n/cupertino_cy.arb', - ]; - final List result = await getTargetsToRun(targets, files); - expect(result, targets); - }); - - test('returns builders when run_if matches files with * trailing glob 2', () async { - final List targets = [ - generateTarget( - 1, - runIf: [ - 'packages/flutter_localizations/**/l10n/cupertino*', - ], - ), - ]; - final List files = [ - 'packages/flutter_localizations/lib/src/l10n/material_es.arb', - 'packages/flutter_localizations/lib/src/l10n/material_en_ZA.arb', - 'packages/flutter_localizations/lib/src/l10n/cupertino_cy.arb', - ]; - final List result = await getTargetsToRun(targets, files); - expect(result, targets); - }); - - test('returns builders when run_if matches files with ** in the middle', () async { - final List targets = [ - generateTarget(1, runIf: ['abc/**/hj.dart']), - ]; - final List files = ['abc/cdf/efg/hj.dart', 'abc/dej.dart']; - final List result = await getTargetsToRun(targets, files); - expect(result, [targets[0]]); - }); - - test('returns builders when run_if matches files with both * and **', () async { - final List targets = [ - generateTarget(1, runIf: ['a/b*c/**']), - ]; - final List files = ['a/baddsc/defg.zz', 'c/d']; - final List result = await getTargetsToRun(targets, files); - expect(result, targets); - }); - - test('returns correct builders when file and folder share the same name', () async { - final List targets = [ - generateTarget(1, runIf: ['a/b/']), - generateTarget(2, runIf: ['a']), - ]; - final List files = ['a']; - final List result = await getTargetsToRun(targets, files); - expect(result.length, 1); - expect(result.single, targets[1]); - }); - - test('run_if takes precedence over run_if_not', () async { - final List targets = [ - generateTarget(1, runIf: ['a/b/']), - generateTarget(2, runIf: ['a'], runIfNot: ['a']), - ]; - final List files = ['a']; - final List result = await getTargetsToRun(targets, files); - expect(result.length, 1); - expect(result.single, targets[1]); - }); - - test('no run_if and not run_if_not', () async { - final List targets = [ - generateTarget(1), - ]; - final List files = ['a']; - final List result = await getTargetsToRun(targets, files); - expect(result.length, 1); - expect(result.single, targets[0]); - }); - - test('run_if_not with matches', () async { - final List targets = [ - generateTarget(1, runIfNot: ['/a/b/**']), - ]; - final List files = ['/a/b/c/d']; - final List result = await getTargetsToRun(targets, files); - expect(result.length, 0); - }); - - test('run_if_not with no matches', () async { - final List targets = [ - generateTarget(1, runIfNot: ['/a/b/**']), - ]; - final List files = ['/a/c']; - final List result = await getTargetsToRun(targets, files); - expect(result.length, 1); - expect(result.single, targets[0]); - }); - }); - }); -} diff --git a/app_dart/test/model/buildbucket_test.dart b/app_dart/test/model/buildbucket_test.dart deleted file mode 100644 index 704f3a100..000000000 --- a/app_dart/test/model/buildbucket_test.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:convert'; - -import 'package:cocoon_service/src/model/common/json_converters.dart'; -import 'package:cocoon_service/src/model/luci/buildbucket.dart'; -import 'package:test/test.dart'; - -void main() { - const String tagsJson = '[' - '{"key":"tag_a","value":"chrome/win32-builder-perf"},' - '{"key":"tag_b","value":"true"},' - '{"key":"tag_b","value":"9083774268329986752"}' - ']'; - - const Map> tags = >{ - 'tag_a': ['chrome/win32-builder-perf'], - 'tag_b': ['true', '9083774268329986752'], - }; - - test('Deserializes tags', () { - final List? decodedTags = json.decode(tagsJson) as List?; - expect(const TagsConverter().fromJson(decodedTags), tags); - }); - - test('Serializes tags', () { - final List> encodedTags = - const TagsConverter().toJson(tags)!.cast>().toList(); - expect(encodedTags.length, 3); - expect(json.encode(encodedTags), tagsJson); - }); - - test('Handles Build id correctly', () { - final String id = 0xFFFFFFFFFFFFFFFF.toString(); // would overflow a 32 bit int - final Build build = Build(id: id, builderId: const BuilderId()); - final Map buildJson = build.toJson(); - expect(buildJson['id'], id.toString()); - expect(buildJson['id'].runtimeType, String); - - final Build deserializedBuild = Build.fromJson(json.decode(json.encode(buildJson)) as Map); - expect(deserializedBuild.id, id); - - final GetBuildRequest request = GetBuildRequest(id: id); - final Map requestBuildJson = request.toJson(); - expect(requestBuildJson['id'], id.toString()); - expect(requestBuildJson['id'].runtimeType, String); - - final GetBuildRequest deserializedRequest = GetBuildRequest.fromJson(requestBuildJson); - expect(deserializedRequest.id, id); - }); - - test('Handles fields correctly', () { - GetBuildRequest request = const GetBuildRequest(id: '9083774268329986752'); - Map requestBuildJson = request.toJson(); - expect(requestBuildJson['id'], request.id.toString()); - request = const GetBuildRequest(id: '9083774268329986752', fields: 'summaryMarkDown'); - requestBuildJson = request.toJson(); - expect(requestBuildJson['id'], request.id.toString()); - expect(requestBuildJson['fields'], 'summaryMarkDown'); - }); - - test('Creates a ScheduleBuildRequest', () { - const ScheduleBuildRequest req = ScheduleBuildRequest( - builderId: BuilderId( - project: 'flutter', - bucket: 'try', - builder: 'fake_builder', - ), - properties: { - 'git_url': 'https://github.com/flutter/flutter', - 'git_ref': 'refs/pull/63834/head', - }, - dimensions: [RequestedDimension(key: 'a', value: 'b', expiration: '120s')], - priority: 100, - ); - expect( - json.encode(req.toJson()), - '{"builder":{"project":"flutter","bucket":"try","builder":"fake_builder"},' - '"properties":{"git_url":"https://github.com/flutter/flutter","git_ref":"refs/pull/63834/head"},' - '"dimensions":[{"key":"a","value":"b","expiration":"120s"}],"priority":100}'); - }); -} diff --git a/app_dart/test/model/ci_yaml/ci_yaml_test.dart b/app_dart/test/model/ci_yaml/ci_yaml_test.dart deleted file mode 100644 index d927755f0..000000000 --- a/app_dart/test/model/ci_yaml/ci_yaml_test.dart +++ /dev/null @@ -1,346 +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/model/ci_yaml/ci_yaml.dart'; -import 'package:cocoon_service/src/model/ci_yaml/target.dart'; -import 'package:cocoon_service/protos.dart' as pb; -import 'package:cocoon_service/src/service/config.dart'; -import 'package:test/test.dart'; - -import '../../src/service/fake_scheduler.dart'; - -void main() { - group('enabledBranchesMatchesCurrentBranch', () { - final List tests = [ - EnabledBranchesRegexTest('matches main', 'main', ['main']), - EnabledBranchesRegexTest( - 'matches candidate branch', - 'flutter-2.4-candidate.3', - ['flutter-\\d+\\.\\d+-candidate\\.\\d+'], - ), - EnabledBranchesRegexTest('matches main when not first pattern', 'main', ['dev', 'main']), - EnabledBranchesRegexTest('does not do partial matches', 'super-main', ['main'], false), - ]; - - for (EnabledBranchesRegexTest regexTest in tests) { - test(regexTest.name, () { - expect( - CiYaml.enabledBranchesMatchesCurrentBranch(regexTest.enabledBranches, regexTest.branch), - regexTest.expectation, - ); - }); - } - }); - - group('Validate pinned version operation.', () { - void validatePinnedVersion(String input) { - test('$input -> returns normally', () { - DependencyValidator.hasVersion(dependencyJsonString: input); - }); - } - - validatePinnedVersion('[{"dependency": "chrome_and_driver", "version": "version:96.2"}]'); - validatePinnedVersion('[{"dependency": "open_jdk", "version": "11"}]'); - validatePinnedVersion('[{"dependency": "android_sdk", "version": "version:31v8"}]'); - validatePinnedVersion( - '[{"dependency": "goldctl", "version": "git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603"}]', - ); - }); - - group('Validate un-pinned version operation.', () { - void validateUnPinnedVersion(String input) { - test('$input -> returns normally', () { - expect(() => DependencyValidator.hasVersion(dependencyJsonString: input), throwsException); - }); - } - - validateUnPinnedVersion('[{"dependency": "some_sdk", "version": ""}]'); - validateUnPinnedVersion('[{"dependency": "another_sdk"}]'); - validateUnPinnedVersion('[{"dependency": "yet_another_sdk", "version": "latest"}]'); - }); - - group('initialTargets', () { - test('targets without deps', () { - final CiYaml ciYaml = exampleConfig; - final List initialTargets = ciYaml.getInitialTargets(ciYaml.postsubmitTargets); - final List initialTargetNames = initialTargets.map((Target target) => target.value.name).toList(); - expect( - initialTargetNames, - containsAll( - [ - 'Linux A', - 'Mac A', - 'Windows A', - ], - ), - ); - }); - - test('filter bringup targets on release branches', () { - final CiYaml ciYaml = CiYaml( - slug: Config.flutterSlug, - branch: Config.defaultBranch(Config.flutterSlug), - config: pb.SchedulerConfig( - enabledBranches: [ - Config.defaultBranch(Config.flutterSlug), - ], - targets: [ - pb.Target( - name: 'Linux A', - ), - pb.Target( - name: 'Mac A', // Should be ignored on release branches - bringup: true, - ), - ], - ), - ); - final List initialTargets = ciYaml.getInitialTargets(ciYaml.postsubmitTargets); - final List initialTargetNames = initialTargets.map((Target target) => target.value.name).toList(); - expect( - initialTargetNames, - containsAll( - [ - 'Linux A', - ], - ), - ); - }); - - group('validations and filters.', () { - final CiYaml totCIYaml = CiYaml( - slug: Config.flutterSlug, - branch: Config.defaultBranch(Config.flutterSlug), - config: pb.SchedulerConfig( - enabledBranches: [ - Config.defaultBranch(Config.flutterSlug), - ], - targets: [ - pb.Target( - name: 'Linux A', - ), - pb.Target( - name: 'Mac A', // Should be ignored on release branches - bringup: true, - ), - ], - ), - ); - final CiYaml ciYaml = CiYaml( - slug: Config.flutterSlug, - branch: 'flutter-2.4-candidate.3', - config: pb.SchedulerConfig( - enabledBranches: [ - 'flutter-2.4-candidate.3', - ], - targets: [ - pb.Target( - name: 'Linux A', - ), - pb.Target( - name: 'Linux B', - ), - pb.Target( - name: 'Mac A', // Should be ignored on release branches - bringup: true, - ), - ], - ), - totConfig: totCIYaml, - ); - - test('filter targets removed from presubmit', () { - final List initialTargets = ciYaml.presubmitTargets; - final List initialTargetNames = initialTargets.map((Target target) => target.value.name).toList(); - expect( - initialTargetNames, - containsAll( - [ - 'Linux A', - ], - ), - ); - }); - - test('filter targets removed from postsubmit', () { - final List initialTargets = ciYaml.postsubmitTargets; - final List initialTargetNames = initialTargets.map((Target target) => target.value.name).toList(); - expect( - initialTargetNames, - containsAll( - [ - 'Linux A', - ], - ), - ); - }); - - test('Get backfill targets from postsubmit', () { - final CiYaml ciYaml = exampleBackfillConfig; - final List backfillTargets = ciYaml.backfillTargets; - final List backfillTargetNames = backfillTargets.map((Target target) => target.value.name).toList(); - expect( - backfillTargetNames, - containsAll( - [ - 'Linux A', - 'Mac A', - ], - ), - ); - }); - - test('filter release_build targets from release candidate branches', () { - final CiYaml releaseYaml = CiYaml( - slug: Config.flutterSlug, - branch: 'flutter-2.4-candidate.3', - config: pb.SchedulerConfig( - enabledBranches: [ - 'flutter-2.4-candidate.3', - ], - targets: [ - pb.Target( - name: 'Linux A', - properties: {'release_build': 'true'}, - ), - pb.Target( - name: 'Linux B', - ), - pb.Target( - name: 'Mac A', // Should be ignored on release branches - bringup: true, - ), - ], - ), - totConfig: totCIYaml, - ); - final List initialTargets = releaseYaml.postsubmitTargets; - final List initialTargetNames = initialTargets.map((Target target) => target.value.name).toList(); - expect(initialTargetNames, isEmpty); - }); - - test('release_build targets for main are not filtered', () { - final CiYaml releaseYaml = CiYaml( - slug: Config.flutterSlug, - branch: 'main', - config: pb.SchedulerConfig( - targets: [ - pb.Target( - name: 'Linux A', - properties: {'release_build': 'true'}, - ), - pb.Target( - name: 'Linux B', - ), - pb.Target( - name: 'Mac A', // Should be ignored on release branches - bringup: true, - ), - ], - ), - totConfig: totCIYaml, - ); - final List initialTargets = releaseYaml.postsubmitTargets; - final List initialTargetNames = initialTargets.map((Target target) => target.value.name).toList(); - expect( - initialTargetNames, - containsAll( - [ - 'Linux A', - ], - ), - ); - }); - - test('validates yaml config', () { - expect( - () => CiYaml( - slug: Config.flutterSlug, - branch: Config.defaultBranch(Config.flutterSlug), - config: pb.SchedulerConfig( - enabledBranches: [ - Config.defaultBranch(Config.flutterSlug), - ], - targets: [ - pb.Target( - name: 'Linux A', - ), - pb.Target( - name: 'Linux B', - ), - ], - ), - totConfig: totCIYaml, - validate: true, - ), - throwsA(isA()), - ); - }); - }); - group('Presubmit validation', () { - final CiYaml totCIYaml = CiYaml( - slug: Config.flutterSlug, - branch: Config.defaultBranch(Config.flutterSlug), - config: pb.SchedulerConfig( - enabledBranches: [ - Config.defaultBranch(Config.flutterSlug), - ], - targets: [ - pb.Target( - name: 'Linux A', - presubmit: false, - ), - pb.Target( - name: 'Mac A', // Should be ignored on release branches - bringup: true, - ), - ], - ), - ); - final CiYaml ciYaml = CiYaml( - slug: Config.flutterSlug, - branch: 'flutter-2.4-candidate.3', - config: pb.SchedulerConfig( - targets: [ - pb.Target( - name: 'Linux A', - presubmit: true, - ), - pb.Target( - name: 'Linux B', - ), - pb.Target( - name: 'Mac A', // Should be ignored on release branches - bringup: true, - ), - ], - ), - totConfig: totCIYaml, - ); - - test('presubmit true target is scheduled though TOT is with presubmit false', () { - final List initialTargets = ciYaml.presubmitTargets; - final List initialTargetNames = initialTargets.map((Target target) => target.value.name).toList(); - expect( - initialTargetNames, - containsAll( - [ - 'Linux A', - ], - ), - ); - }); - }); - }); -} - -/// Wrapper class for table driven design of [CiYaml.enabledBranchesMatchesCurrentBranch]. -class EnabledBranchesRegexTest { - EnabledBranchesRegexTest(this.name, this.branch, this.enabledBranches, [this.expectation = true]); - - final String branch; - final List enabledBranches; - final String name; - final bool expectation; -} diff --git a/app_dart/test/model/ci_yaml/target_test.dart b/app_dart/test/model/ci_yaml/target_test.dart deleted file mode 100644 index d06d57d0e..000000000 --- a/app_dart/test/model/ci_yaml/target_test.dart +++ /dev/null @@ -1,234 +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/model/ci_yaml/target.dart'; -import 'package:cocoon_service/src/model/luci/buildbucket.dart'; -import 'package:cocoon_service/src/model/proto/protos.dart' as pb; -import 'package:cocoon_service/src/service/scheduler/policy.dart'; -import 'package:github/github.dart' as github; -import 'package:test/test.dart'; - -import '../../src/utilities/entity_generators.dart'; - -void main() { - group('Target', () { - group('properties', () { - test('default properties', () { - final Target target = generateTarget(1); - expect(target.getProperties(), { - 'bringup': false, - 'dependencies': [], - }); - }); - - test('properties with ignore_flakiness true', () { - final Target target = generateTarget( - 1, - platform: 'Mac_ios', - properties: { - 'ignore_flakiness': 'true', - }, - ); - expect(target.getIgnoreFlakiness(), true); - }); - - test('properties with ignore_flakiness not present', () { - final Target target = generateTarget( - 1, - platform: 'Mac_ios', - platformProperties: { - // This should be overrided by the target specific property - 'xcode': 'abc', - }, - ); - expect(target.getIgnoreFlakiness(), false); - }); - - test('properties with ignore_flakiness in platform properties', () { - final Target target = generateTarget( - 1, - platform: 'Mac_ios', - platformProperties: { - // This should be overrided by the target specific property - 'ignore_flakiness': 'true', - }, - ); - expect(target.getIgnoreFlakiness(), true); - }); - - test('properties with ignore_flakiness overrides platform properties', () { - final Target target = generateTarget( - 1, - platform: 'Mac_ios', - platformProperties: { - // This should be overrided by the target specific property - 'ignore_flakiness': 'true', - }, - properties: { - 'ignore_flakiness': 'false', - }, - ); - expect(target.getIgnoreFlakiness(), false); - }); - - test('properties with \$flutter/osx_sdk overrides platform properties', () { - final Target target = generateTarget( - 1, - platform: 'Mac', - platformProperties: { - // This should be overrided by the target specific property - '\$flutter/osx_sdk': '{"sdk_version": "12abc", "runtime_versions": ["ios-11-0", "ios-12-0"]}', - }, - properties: { - '\$flutter/osx_sdk': '{"sdk_version": "14e222b", "runtime_versions": ["ios-13-0", "ios-15-0"]}', - }, - ); - expect(target.getProperties(), { - 'bringup': false, - 'dependencies': [], - '\$flutter/osx_sdk': { - 'runtime_versions': ['ios-13-0', 'ios-15-0'], - 'sdk_version': '14e222b', - }, - }); - }); - - test('tags are parsed from within properties', () { - final Target target = generateTarget( - 1, - platform: 'Linux_build_test', - platformProperties: { - // This should be overrided by the target specific property - 'android_sdk': 'abc', - }, - properties: {'xcode': '12abc', 'tags': '["devicelab", "android", "linux"]'}, - ); - expect(target.tags, ['devicelab', 'android', 'linux']); - }); - - test('we do not blow up if tags are not present', () { - final Target target = generateTarget( - 1, - platform: 'Linux_build_test', - platformProperties: { - // This should be overrided by the target specific property - 'android_sdk': 'abc', - }, - ); - expect(target.tags, []); - }); - }); - - group('dimensions', () { - test('no dimensions', () { - final Target target = generateTarget(1); - expect(target.getDimensions().length, 0); - }); - - test('platform dimensions and target dimensions are combined', () { - final Target target = generateTarget( - 1, - platform: 'Mac_ios', - platformDimensions: { - 'signing_cert': 'none', - }, - properties: {'os': 'abc', 'cpu': 'x64'}, - ); - final List dimensions = target.getDimensions(); - expect(dimensions.length, 3); - expect(dimensions[0].key, 'signing_cert'); - expect(dimensions[0].value, 'none'); - expect(dimensions[1].key, 'os'); - expect(dimensions[1].value, 'abc'); - expect(dimensions[2].key, 'cpu'); - expect(dimensions[2].value, 'x64'); - }); - - test('target specific dimensions overrides platform dimensions', () { - final Target target = generateTarget( - 1, - platform: 'Mac_ios', - platformDimensions: { - 'signing_cert': 'none', - }, - dimensions: {'signing_cert': 'mac'}, - ); - final List dimensions = target.getDimensions(); - expect(dimensions.length, 1); - expect(dimensions[0].key, 'signing_cert'); - expect(dimensions[0].value, 'mac'); - }); - - test('target specific dimensions overrides legacy target specific properties', () { - final Target target = generateTarget( - 1, - platform: 'Windows', - dimensions: {'cpu': 'x64'}, - properties: {'cpu': 'x32'}, - ); - final List dimensions = target.getDimensions(); - expect(dimensions.length, 1); - expect(dimensions[0].key, 'cpu'); - expect(dimensions[0].value, 'x64'); - }); - - test('target specific dimensions overrides legacy platform properties', () { - final Target target = generateTarget( - 1, - platform: 'Windows', - dimensions: {'cpu': 'x64'}, - platformProperties: {'cpu': 'x32'}, - ); - final List dimensions = target.getDimensions(); - expect(dimensions.length, 1); - expect(dimensions[0].key, 'cpu'); - expect(dimensions[0].value, 'x64'); - }); - - test('properties are evaluated as string', () { - final Target target = generateTarget( - 1, - platform: 'Mac_ios', - platformDimensions: { - 'signing_cert': 'none', - }, - properties: {'cores': '32'}, - ); - expect(target.getDimensions().length, 2); - }); - }); - - group('scheduler policy', () { - test('devicelab targets use batch policy', () { - expect(generateTarget(1, platform: 'Linux_android').schedulerPolicy, isA()); - }); - - test('devicelab samsung targets use batch policy', () { - expect(generateTarget(1, platform: 'Linux_samsung_a02').schedulerPolicy, isA()); - }); - - test('mac host only targets use batch policy', () { - expect(generateTarget(1, platform: 'Mac').schedulerPolicy, isA()); - }); - - test('non-cocoon scheduler targets return omit policy', () { - expect( - generateTarget(1, platform: 'Linux_android', schedulerSystem: pb.SchedulerSystem.luci).schedulerPolicy, - isA(), - ); - }); - - test('vm cocoon targets return batch policy', () { - expect(generateTarget(1, platform: 'Linux').schedulerPolicy, isA()); - }); - - test('packages targets use guaranteed policy', () { - expect( - generateTarget(1, platform: 'Mac', slug: github.RepositorySlug('flutter', 'packages')).schedulerPolicy, - isA(), - ); - }); - }); - }); -} diff --git a/app_dart/test/model/commit_test.dart b/app_dart/test/model/commit_test.dart deleted file mode 100644 index 3907e77a2..000000000 --- a/app_dart/test/model/commit_test.dart +++ /dev/null @@ -1,61 +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/model/appengine/commit.dart'; -import 'package:cocoon_service/src/service/datastore.dart'; -import 'package:gcloud/db.dart'; -import 'package:github/github.dart'; -import 'package:test/test.dart'; - -import '../src/datastore/fake_config.dart'; -import '../src/datastore/fake_datastore.dart'; -import '../src/utilities/entity_generators.dart'; - -void main() { - group('Commit.composeKey', () { - test('creates valid key', () { - final FakeDatastoreDB db = FakeDatastoreDB(); - final RepositorySlug slug = RepositorySlug('flutter', 'flutter'); - const String gitBranch = 'main'; - const String sha = 'abc'; - final key = Commit.createKey(db: db, slug: slug, gitBranch: gitBranch, sha: sha); - expect(key.id, equals('flutter/flutter/main/abc')); - }); - }); - - group('Commit.fromDatastore', () { - late FakeConfig config; - late Commit expectedCommit; - - setUp(() { - config = FakeConfig(); - expectedCommit = generateCommit(1); - config.db.values[expectedCommit.key] = expectedCommit; - }); - - test('look up by id', () async { - final Commit commit = await Commit.fromDatastore( - datastore: DatastoreService(config.db, 5), - key: expectedCommit.key, - ); - expect(commit, expectedCommit); - }); - - test('look up by id fails if cannot be found', () async { - final datastore = DatastoreService(config.db, 5); - expect( - Commit.fromDatastore( - datastore: datastore, - key: Commit.createKey( - db: datastore.db, - slug: RepositorySlug('abc', 'test'), - gitBranch: 'main', - sha: 'def', - ), - ), - throwsA(isA()), - ); - }); - }); -} diff --git a/app_dart/test/model/gerrit/commit_test.dart b/app_dart/test/model/gerrit/commit_test.dart deleted file mode 100644 index 243377c1e..000000000 --- a/app_dart/test/model/gerrit/commit_test.dart +++ /dev/null @@ -1,37 +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/gerrit/commit.dart'; -import 'package:test/test.dart'; - -void main() { - group(GerritCommit, () { - test('fromJson', () { - const String json = '''{ - "commit": "c80a772eebe7f47d12ad1b21bc48fbd9521519aa", - "tree": "ee444f607795706641aedbc7c43a578b001aec5e", - "parents": [ - "3770382108d17154bbacce45dd18e475718cd904" - ], - "author": { - "name": "recipe-roller", - "email": "flutter-prod-builder@chops-service-accounts.iam.gserviceaccount.com", - "time": "Wed Jun 07 22:54:06 2023 +0000" - }, - "committer": { - "name": "CQ Bot Account", - "email": "flutter-scoped@luci-project-accounts.iam.gserviceaccount.com", - "time": "Wed Jun 07 22:54:06 2023 +0000" - }, - "message": "Roll recipe dependencies (trivial)\\n\\nThis is an automated CL created by the recipe roller." - }'''; - final GerritCommit commit = GerritCommit.fromJson(jsonDecode(json)); - expect(commit.author, isNotNull); - expect(commit.author!.name, 'recipe-roller'); - expect(commit.author!.time, DateTime(2023, 06, 07, 22, 54, 6)); - }); - }); -} diff --git a/app_dart/test/model/github/checks_test_data.dart b/app_dart/test/model/github/checks_test_data.dart deleted file mode 100644 index 337467ec0..000000000 --- a/app_dart/test/model/github/checks_test_data.dart +++ /dev/null @@ -1,822 +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. - -String checkSuiteString = checkSuiteTemplate('requested'); - -String checkSuiteTemplate(String action) => '''\ -{ - "action": "$action", - "check_suite": { - "id": 694267587, - "node_id": "MDEwOkNoZWNrU3VpdGU2OTQyNjc1ODc=", - "head_branch": "update_licenses", - "head_sha": "dabc07b74c555c9952f7b63e139f2bb83b75250f", - "status": "queued", - "conclusion": "success", - "url": "https://api.github.com/repos/flutter/cocoon/check-suites/694267587", - "before": "5763f4c2b3b5e529f4b35c655761a7e818eced2e", - "after": "dabc07b74c555c9952f7b63e139f2bb83b75250f", - "pull_requests": [ - { - "url": "https://api.github.com/repos/flutter/cocoon/pulls/758", - "id": 409012032, - "number": 758, - "head": { - "ref": "update_licenses", - "sha": "5763f4c2b3b5e529f4b35c655761a7e818eced2e", - "repo": { - "id": 212688278, - "url": "https://api.github.com/repos/flutter/cocoon", - "name": "cocoon" - } - }, - "base": { - "ref": "main", - "sha": "cc430b2e8d6448dfbacf5bcbbd6160cd1fe9dc0b", - "repo": { - "id": 63260554, - "url": "https://api.github.com/repos/flutter/cocoon", - "name": "cocoon", - "owner": { - "avatar_url": "", - "html_url": "", - "login": "flutter", - "id": 54371434 - } - } - } - } - ], - "app": { - "id": 64368, - "slug": "test", - "node_id": "MDM6QXBwNjQzNjg=", - "owner": { - "login": "abc", - "id": 54371434, - "node_id": "MDQ6VXNlcjU0MzcxNDM0", - "avatar_url": "https://avatars3.githubusercontent.com/u/54371434?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/abc", - "html_url": "https://github.com/abc", - "followers_url": "https://api.github.com/users/abc/followers", - "following_url": "https://api.github.com/users/abc/following{/other_user}", - "gists_url": "https://api.github.com/users/abc/gists{/gist_id}", - "starred_url": "https://api.github.com/users/abc/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/abc/subscriptions", - "organizations_url": "https://api.github.com/users/abc/orgs", - "repos_url": "https://api.github.com/users/abc/repos", - "events_url": "https://api.github.com/users/abc/events{/privacy}", - "received_events_url": "https://api.github.com/users/abc/received_events", - "type": "User", - "site_admin": false - }, - "name": "godofredo-test", - "description": "", - "external_url": "https://flutter-dashboard.appspot.com", - "html_url": "https://github.com/apps/test", - "created_at": "2020-05-10T00:32:46Z", - "updated_at": "2020-05-10T00:32:46Z", - "permissions": { - "checks": "write", - "contents": "read", - "metadata": "read" - }, - "events": [ - "check_run", - "check_suite", - "label" - ] - }, - "created_at": "2020-05-18T23:04:25Z", - "updated_at": "2020-05-18T23:04:25Z", - "latest_check_runs_count": 0, - "check_runs_url": "https://api.github.com/repos/flutter/cocoon/check-suites/694267587/check-runs", - "head_commit": { - "id": "dabc07b74c555c9952f7b63e139f2bb83b75250f", - "tree_id": "5f8bd91387bbb9b5db90ab63ac9229224d1b6044", - "message": "Add checks and tests for license folder.", - "timestamp": "2020-05-18T23:03:38Z", - "author": { - "name": "abc", - "email": "abc@abcd.com" - }, - "committer": { - "name": "abc", - "email": "abc@abcd.com" - } - } - }, - "repository": { - "id": 212688278, - "node_id": "MDEwOlJlcG9zaXRvcnkyMTI2ODgyNzg=", - "name": "cocoon", - "full_name": "flutter/cocoon", - "private": false, - "owner": { - "login": "abc", - "id": 54371434, - "node_id": "MDQ6VXNlcjU0MzcxNDM0", - "avatar_url": "https://avatars3.githubusercontent.com/u/54371434?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/abc", - "html_url": "https://github.com/abc", - "followers_url": "https://api.github.com/users/abc/followers", - "following_url": "https://api.github.com/users/abc/following{/other_user}", - "gists_url": "https://api.github.com/users/abc/gists{/gist_id}", - "starred_url": "https://api.github.com/users/abc/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/abc/subscriptions", - "organizations_url": "https://api.github.com/users/abc/orgs", - "repos_url": "https://api.github.com/users/abc/repos", - "events_url": "https://api.github.com/users/abc/events{/privacy}", - "received_events_url": "https://api.github.com/users/abc/received_events", - "type": "User", - "site_admin": false - }, - "html_url": "https://github.com/flutter/cocoon", - "description": "Flutter's build coordinator and aggregator", - "fork": true, - "url": "https://api.github.com/repos/flutter/cocoon", - "forks_url": "https://api.github.com/repos/flutter/cocoon/forks", - "keys_url": "https://api.github.com/repos/flutter/cocoon/keys{/key_id}", - "collaborators_url": "https://api.github.com/repos/flutter/cocoon/collaborators{/collaborator}", - "teams_url": "https://api.github.com/repos/flutter/cocoon/teams", - "hooks_url": "https://api.github.com/repos/flutter/cocoon/hooks", - "issue_events_url": "https://api.github.com/repos/flutter/cocoon/issues/events{/number}", - "events_url": "https://api.github.com/repos/flutter/cocoon/events", - "assignees_url": "https://api.github.com/repos/flutter/cocoon/assignees{/user}", - "branches_url": "https://api.github.com/repos/flutter/cocoon/branches{/branch}", - "tags_url": "https://api.github.com/repos/flutter/cocoon/tags", - "blobs_url": "https://api.github.com/repos/flutter/cocoon/git/blobs{/sha}", - "git_tags_url": "https://api.github.com/repos/flutter/cocoon/git/tags{/sha}", - "git_refs_url": "https://api.github.com/repos/flutter/cocoon/git/refs{/sha}", - "trees_url": "https://api.github.com/repos/flutter/cocoon/git/trees{/sha}", - "statuses_url": "https://api.github.com/repos/flutter/cocoon/statuses/{sha}", - "languages_url": "https://api.github.com/repos/flutter/cocoon/languages", - "stargazers_url": "https://api.github.com/repos/flutter/cocoon/stargazers", - "contributors_url": "https://api.github.com/repos/flutter/cocoon/contributors", - "subscribers_url": "https://api.github.com/repos/flutter/cocoon/subscribers", - "subscription_url": "https://api.github.com/repos/flutter/cocoon/subscription", - "commits_url": "https://api.github.com/repos/flutter/cocoon/commits{/sha}", - "git_commits_url": "https://api.github.com/repos/flutter/cocoon/git/commits{/sha}", - "comments_url": "https://api.github.com/repos/flutter/cocoon/comments{/number}", - "issue_comment_url": "https://api.github.com/repos/flutter/cocoon/issues/comments{/number}", - "contents_url": "https://api.github.com/repos/flutter/cocoon/contents/{+path}", - "compare_url": "https://api.github.com/repos/flutter/cocoon/compare/{base}...{head}", - "merges_url": "https://api.github.com/repos/flutter/cocoon/merges", - "archive_url": "https://api.github.com/repos/flutter/cocoon/{archive_format}{/ref}", - "downloads_url": "https://api.github.com/repos/flutter/cocoon/downloads", - "issues_url": "https://api.github.com/repos/flutter/cocoon/issues{/number}", - "pulls_url": "https://api.github.com/repos/flutter/cocoon/pulls{/number}", - "milestones_url": "https://api.github.com/repos/flutter/cocoon/milestones{/number}", - "notifications_url": "https://api.github.com/repos/flutter/cocoon/notifications{?since,all,participating}", - "labels_url": "https://api.github.com/repos/flutter/cocoon/labels{/name}", - "releases_url": "https://api.github.com/repos/flutter/cocoon/releases{/id}", - "deployments_url": "https://api.github.com/repos/flutter/cocoon/deployments", - "created_at": "2019-10-03T21:57:12Z", - "updated_at": "2019-10-03T21:57:14Z", - "pushed_at": "2020-05-18T23:04:24Z", - "git_url": "git://github.com/flutter/cocoon.git", - "ssh_url": "git@github.com:flutter/cocoon.git", - "clone_url": "https://github.com/flutter/cocoon.git", - "svn_url": "https://github.com/flutter/cocoon", - "homepage": null, - "size": 3070, - "stargazers_count": 0, - "watchers_count": 0, - "language": null, - "has_issues": false, - "has_projects": true, - "has_downloads": true, - "has_wiki": false, - "has_pages": false, - "forks_count": 0, - "mirror_url": null, - "archived": false, - "disabled": false, - "open_issues_count": 1, - "license": { - "key": "clause", - "name": "License", - "spdx_id": "", - "url": "", - "node_id": "MDc6TGljZW5zZTU=" - }, - "forks": 0, - "open_issues": 1, - "watchers": 0, - "default_branch": "main" - }, - "sender": { - "login": "abc", - "id": 54371434, - "node_id": "MDQ6VXNlcjU0MzcxNDM0", - "avatar_url": "https://avatars3.githubusercontent.com/u/54371434?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/abc", - "html_url": "https://github.com/abc", - "followers_url": "https://api.github.com/users/abc/followers", - "following_url": "https://api.github.com/users/abc/following{/other_user}", - "gists_url": "https://api.github.com/users/abc/gists{/gist_id}", - "starred_url": "https://api.github.com/users/abc/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/abc/subscriptions", - "organizations_url": "https://api.github.com/users/abc/orgs", - "repos_url": "https://api.github.com/users/abc/repos", - "events_url": "https://api.github.com/users/abc/events{/privacy}", - "received_events_url": "https://api.github.com/users/abc/received_events", - "type": "User", - "site_admin": false - }, - "installation": { - "id": 8770981, - "node_id": "MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uODc3MDk4MQ==" - } -} -'''; - -const String checkRunString = ''' -{ - "action": "rerequested", - "check_run": { - "id": 660053389, - "node_id": "MDg6Q2hlY2tSdW42NjAwNTMzODk=", - "head_sha": "66d6bd9a3f79a36fe4f5178ccefbc781488a596c", - "external_id": "", - "url": "https://api.github.com/repos/flutter/cocoon/check-runs/660053389", - "html_url": "https://github.com/flutter/cocoon/runs/660053389", - "details_url": "https://flutter-dashboard.appspot.com", - "status": "completed", - "conclusion": "success", - "started_at": "2020-05-10T02:49:31Z", - "completed_at": "2020-05-10T03:11:08Z", - "output": { - "title": null, - "summary": null, - "text": null, - "annotations_count": 0, - "annotations_url": "https://api.github.com/repos/flutter/cocoon/check-runs/660053389/annotations" - }, - "name": "test1", - "check_suite": { - "id": 668083231, - "node_id": "MDEwOkNoZWNrU3VpdGU2NjgwODMyMzE=", - "head_branch": "independent_agent", - "head_sha": "66d6bd9a3f79a36fe4f5178ccefbc781488a596c", - "status": "queued", - "conclusion": null, - "url": "https://api.github.com/repos/flutter/cocoon/check-suites/668083231", - "before": "918f7fdf0337dac0fca0254e1b0e46e79f8e7a37", - "after": "66d6bd9a3f79a36fe4f5178ccefbc781488a596c", - "pull_requests": [ - { - "url": "https://api.github.com/repos/flutter/cocoon/pulls/1", - "id": 415645312, - "number": 1, - "head": { - "ref": "independent_agent", - "sha": "66d6bd9a3f79a36fe4f5178ccefbc781488a596c", - "repo": { - "id": 212688278, - "url": "https://api.github.com/repos/flutter/cocoon", - "name": "cocoon" - } - }, - "base": { - "ref": "main", - "sha": "96b953d99588ade4a2b5e9c920813f8f3841b7fb", - "repo": { - "id": 212688278, - "url": "https://api.github.com/repos/flutter/cocoon", - "name": "cocoon", - "owner": { - "avatar_url": "", - "html_url": "", - "login": "flutter", - "id": 54371434 - } - } - } - } - ], - "app": { - "id": 64368, - "slug": "godofredo-test", - "node_id": "MDM6QXBwNjQzNjg=", - "owner": { - "login": "abc", - "id": 54371434, - "node_id": "MDQ6VXNlcjU0MzcxNDM0", - "avatar_url": "https://avatars3.githubusercontent.com/u/54371434?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/abc", - "html_url": "https://github.com/abc", - "followers_url": "https://api.github.com/users/abc/followers", - "following_url": "https://api.github.com/users/abc/following{/other_user}", - "gists_url": "https://api.github.com/users/abc/gists{/gist_id}", - "starred_url": "https://api.github.com/users/abc/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/abc/subscriptions", - "organizations_url": "https://api.github.com/users/abc/orgs", - "repos_url": "https://api.github.com/users/abc/repos", - "events_url": "https://api.github.com/users/abc/events{/privacy}", - "received_events_url": "https://api.github.com/users/abc/received_events", - "type": "User", - "site_admin": false - }, - "name": "godofredo-test", - "description": "", - "external_url": "https://flutter-dashboard.appspot.com", - "html_url": "https://github.com/apps/godofredo-test", - "created_at": "2020-05-10T00:32:46Z", - "updated_at": "2020-05-10T00:32:46Z", - "permissions": { - "checks": "write", - "contents": "read", - "metadata": "read" - }, - "events": [ - "check_run", - "check_suite", - "label" - ] - }, - "created_at": "2020-05-10T01:59:58Z", - "updated_at": "2020-05-10T01:59:58Z" - }, - "app": { - "id": 64368, - "slug": "godofredo-test", - "node_id": "MDM6QXBwNjQzNjg=", - "owner": { - "login": "abc", - "id": 54371434, - "node_id": "MDQ6VXNlcjU0MzcxNDM0", - "avatar_url": "https://avatars3.githubusercontent.com/u/54371434?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/abc", - "html_url": "https://github.com/abc", - "followers_url": "https://api.github.com/users/abc/followers", - "following_url": "https://api.github.com/users/abc/following{/other_user}", - "gists_url": "https://api.github.com/users/abc/gists{/gist_id}", - "starred_url": "https://api.github.com/users/abc/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/abc/subscriptions", - "organizations_url": "https://api.github.com/users/abc/orgs", - "repos_url": "https://api.github.com/users/abc/repos", - "events_url": "https://api.github.com/users/abc/events{/privacy}", - "received_events_url": "https://api.github.com/users/abc/received_events", - "type": "User", - "site_admin": false - }, - "name": "godofredo-test", - "description": "", - "external_url": "https://flutter-dashboard.appspot.com", - "html_url": "https://github.com/apps/godofredo-test", - "created_at": "2020-05-10T00:32:46Z", - "updated_at": "2020-05-10T00:32:46Z", - "permissions": { - "checks": "write", - "contents": "read", - "metadata": "read" - }, - "events": [ - "check_run", - "check_suite", - "label" - ] - }, - "pull_requests": [ - { - "url": "https://api.github.com/repos/flutter/cocoon/pulls/1", - "id": 415645312, - "number": 1, - "head": { - "ref": "independent_agent", - "sha": "66d6bd9a3f79a36fe4f5178ccefbc781488a596c", - "repo": { - "id": 212688278, - "url": "https://api.github.com/repos/flutter/cocoon", - "name": "cocoon" - } - }, - "base": { - "ref": "main", - "sha": "96b953d99588ade4a2b5e9c920813f8f3841b7fb", - "repo": { - "id": 212688278, - "url": "https://api.github.com/repos/flutter/cocoon", - "name": "cocoon", - "owner": { - "avatar_url": "", - "html_url": "", - "login": "flutter", - "id": 54371434 - } - } - } - } - ] - }, - "repository": { - "id": 212688278, - "node_id": "MDEwOlJlcG9zaXRvcnkyMTI2ODgyNzg=", - "name": "cocoon", - "full_name": "flutter/cocoon", - "private": false, - "owner": { - "login": "abc", - "id": 54371434, - "node_id": "MDQ6VXNlcjU0MzcxNDM0", - "avatar_url": "https://avatars3.githubusercontent.com/u/54371434?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/abc", - "html_url": "https://github.com/abc", - "followers_url": "https://api.github.com/users/abc/followers", - "following_url": "https://api.github.com/users/abc/following{/other_user}", - "gists_url": "https://api.github.com/users/abc/gists{/gist_id}", - "starred_url": "https://api.github.com/users/abc/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/abc/subscriptions", - "organizations_url": "https://api.github.com/users/abc/orgs", - "repos_url": "https://api.github.com/users/abc/repos", - "events_url": "https://api.github.com/users/abc/events{/privacy}", - "received_events_url": "https://api.github.com/users/abc/received_events", - "type": "User", - "site_admin": false - }, - "html_url": "https://github.com/flutter/cocoon", - "description": "Flutter's build coordinator and aggregator", - "fork": true, - "url": "https://api.github.com/repos/flutter/cocoon", - "forks_url": "https://api.github.com/repos/flutter/cocoon/forks", - "keys_url": "https://api.github.com/repos/flutter/cocoon/keys{/key_id}", - "collaborators_url": "https://api.github.com/repos/flutter/cocoon/collaborators{/collaborator}", - "teams_url": "https://api.github.com/repos/flutter/cocoon/teams", - "hooks_url": "https://api.github.com/repos/flutter/cocoon/hooks", - "issue_events_url": "https://api.github.com/repos/flutter/cocoon/issues/events{/number}", - "events_url": "https://api.github.com/repos/flutter/cocoon/events", - "assignees_url": "https://api.github.com/repos/flutter/cocoon/assignees{/user}", - "branches_url": "https://api.github.com/repos/flutter/cocoon/branches{/branch}", - "tags_url": "https://api.github.com/repos/flutter/cocoon/tags", - "blobs_url": "https://api.github.com/repos/flutter/cocoon/git/blobs{/sha}", - "git_tags_url": "https://api.github.com/repos/flutter/cocoon/git/tags{/sha}", - "git_refs_url": "https://api.github.com/repos/flutter/cocoon/git/refs{/sha}", - "trees_url": "https://api.github.com/repos/flutter/cocoon/git/trees{/sha}", - "statuses_url": "https://api.github.com/repos/flutter/cocoon/statuses/{sha}", - "languages_url": "https://api.github.com/repos/flutter/cocoon/languages", - "stargazers_url": "https://api.github.com/repos/flutter/cocoon/stargazers", - "contributors_url": "https://api.github.com/repos/flutter/cocoon/contributors", - "subscribers_url": "https://api.github.com/repos/flutter/cocoon/subscribers", - "subscription_url": "https://api.github.com/repos/flutter/cocoon/subscription", - "commits_url": "https://api.github.com/repos/flutter/cocoon/commits{/sha}", - "git_commits_url": "https://api.github.com/repos/flutter/cocoon/git/commits{/sha}", - "comments_url": "https://api.github.com/repos/flutter/cocoon/comments{/number}", - "issue_comment_url": "https://api.github.com/repos/flutter/cocoon/issues/comments{/number}", - "contents_url": "https://api.github.com/repos/flutter/cocoon/contents/{+path}", - "compare_url": "https://api.github.com/repos/flutter/cocoon/compare/{base}...{head}", - "merges_url": "https://api.github.com/repos/flutter/cocoon/merges", - "archive_url": "https://api.github.com/repos/flutter/cocoon/{archive_format}{/ref}", - "downloads_url": "https://api.github.com/repos/flutter/cocoon/downloads", - "issues_url": "https://api.github.com/repos/flutter/cocoon/issues{/number}", - "pulls_url": "https://api.github.com/repos/flutter/cocoon/pulls{/number}", - "milestones_url": "https://api.github.com/repos/flutter/cocoon/milestones{/number}", - "notifications_url": "https://api.github.com/repos/flutter/cocoon/notifications{?since,all,participating}", - "labels_url": "https://api.github.com/repos/flutter/cocoon/labels{/name}", - "releases_url": "https://api.github.com/repos/flutter/cocoon/releases{/id}", - "deployments_url": "https://api.github.com/repos/flutter/cocoon/deployments", - "created_at": "2019-10-03T21:57:12Z", - "updated_at": "2019-10-03T21:57:14Z", - "pushed_at": "2020-05-09T23:42:23Z", - "git_url": "git://github.com/flutter/cocoon.git", - "ssh_url": "git@github.com:flutter/cocoon.git", - "clone_url": "https://github.com/flutter/cocoon.git", - "svn_url": "https://github.com/flutter/cocoon", - "homepage": null, - "size": 2941, - "stargazers_count": 0, - "watchers_count": 0, - "language": null, - "has_issues": false, - "has_projects": true, - "has_downloads": true, - "has_wiki": false, - "has_pages": false, - "forks_count": 0, - "mirror_url": null, - "archived": false, - "disabled": false, - "open_issues_count": 1, - "license": { - "key": "", - "name": "", - "spdx_id": "", - "url": "", - "node_id": "MDc6TGljZW5zZTU=" - }, - "forks": 0, - "open_issues": 1, - "watchers": 0, - "default_branch": "main" - }, - "sender": { - "login": "abc", - "id": 54371434, - "node_id": "MDQ6VXNlcjU0MzcxNDM0", - "avatar_url": "https://avatars3.githubusercontent.com/u/54371434?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/abc", - "html_url": "https://github.com/abc", - "followers_url": "https://api.github.com/users/abc/followers", - "following_url": "https://api.github.com/users/abc/following{/other_user}", - "gists_url": "https://api.github.com/users/abc/gists{/gist_id}", - "starred_url": "https://api.github.com/users/abc/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/abc/subscriptions", - "organizations_url": "https://api.github.com/users/abc/orgs", - "repos_url": "https://api.github.com/users/abc/repos", - "events_url": "https://api.github.com/users/abc/events{/privacy}", - "received_events_url": "https://api.github.com/users/abc/received_events", - "type": "User", - "site_admin": false - }, - "installation": { - "id": 8770981, - "node_id": "MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uODc3MDk4MQ==" - } -} -'''; - -const String checkRunWithEmptyPullRequests = ''' -{ - "action": "rerequested", - "check_run": { - "id": 660053389, - "node_id": "MDg6Q2hlY2tSdW42NjAwNTMzODk=", - "head_sha": "66d6bd9a3f79a36fe4f5178ccefbc781488a596c", - "external_id": "", - "url": "https://api.github.com/repos/flutter/cocoon/check-runs/660053389", - "html_url": "https://github.com/flutter/cocoon/runs/660053389", - "details_url": "https://flutter-dashboard.appspot.com", - "status": "completed", - "conclusion": "success", - "started_at": "2020-05-10T02:49:31Z", - "completed_at": "2020-05-10T03:11:08Z", - "output": { - "title": null, - "summary": null, - "text": null, - "annotations_count": 0, - "annotations_url": "https://api.github.com/repos/flutter/cocoon/check-runs/660053389/annotations" - }, - "name": "test1", - "check_suite": { - "id": 668083231, - "node_id": "MDEwOkNoZWNrU3VpdGU2NjgwODMyMzE=", - "head_branch": "independent_agent", - "head_sha": "66d6bd9a3f79a36fe4f5178ccefbc781488a596c", - "status": "queued", - "conclusion": null, - "url": "https://api.github.com/repos/flutter/cocoon/check-suites/668083231", - "before": "918f7fdf0337dac0fca0254e1b0e46e79f8e7a37", - "after": "66d6bd9a3f79a36fe4f5178ccefbc781488a596c", - "pull_requests": [ - { - "url": "https://api.github.com/repos/flutter/cocoon/pulls/1", - "id": 415645312, - "number": 1, - "head": { - "ref": "independent_agent", - "sha": "66d6bd9a3f79a36fe4f5178ccefbc781488a596c", - "repo": { - "id": 212688278, - "url": "https://api.github.com/repos/flutter/cocoon", - "name": "cocoon" - } - }, - "base": { - "ref": "main", - "sha": "96b953d99588ade4a2b5e9c920813f8f3841b7fb", - "repo": { - "id": 212688278, - "url": "https://api.github.com/repos/flutter/cocoon", - "name": "cocoon", - "owner": { - "avatar_url": "", - "html_url": "", - "login": "flutter", - "id": 54371434 - } - } - } - } - ], - "app": { - "id": 64368, - "slug": "godofredo-test", - "node_id": "MDM6QXBwNjQzNjg=", - "owner": { - "login": "abc", - "id": 54371434, - "node_id": "MDQ6VXNlcjU0MzcxNDM0", - "avatar_url": "https://avatars3.githubusercontent.com/u/54371434?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/abc", - "html_url": "https://github.com/abc", - "followers_url": "https://api.github.com/users/abc/followers", - "following_url": "https://api.github.com/users/abc/following{/other_user}", - "gists_url": "https://api.github.com/users/abc/gists{/gist_id}", - "starred_url": "https://api.github.com/users/abc/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/abc/subscriptions", - "organizations_url": "https://api.github.com/users/abc/orgs", - "repos_url": "https://api.github.com/users/abc/repos", - "events_url": "https://api.github.com/users/abc/events{/privacy}", - "received_events_url": "https://api.github.com/users/abc/received_events", - "type": "User", - "site_admin": false - }, - "name": "godofredo-test", - "description": "", - "external_url": "https://flutter-dashboard.appspot.com", - "html_url": "https://github.com/apps/godofredo-test", - "created_at": "2020-05-10T00:32:46Z", - "updated_at": "2020-05-10T00:32:46Z", - "permissions": { - "checks": "write", - "contents": "read", - "metadata": "read" - }, - "events": [ - "check_run", - "check_suite", - "label" - ] - }, - "created_at": "2020-05-10T01:59:58Z", - "updated_at": "2020-05-10T01:59:58Z" - }, - "app": { - "id": 64368, - "slug": "godofredo-test", - "node_id": "MDM6QXBwNjQzNjg=", - "owner": { - "login": "abc", - "id": 54371434, - "node_id": "MDQ6VXNlcjU0MzcxNDM0", - "avatar_url": "https://avatars3.githubusercontent.com/u/54371434?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/abc", - "html_url": "https://github.com/abc", - "followers_url": "https://api.github.com/users/abc/followers", - "following_url": "https://api.github.com/users/abc/following{/other_user}", - "gists_url": "https://api.github.com/users/abc/gists{/gist_id}", - "starred_url": "https://api.github.com/users/abc/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/abc/subscriptions", - "organizations_url": "https://api.github.com/users/abc/orgs", - "repos_url": "https://api.github.com/users/abc/repos", - "events_url": "https://api.github.com/users/abc/events{/privacy}", - "received_events_url": "https://api.github.com/users/abc/received_events", - "type": "User", - "site_admin": false - }, - "name": "godofredo-test", - "description": "", - "external_url": "https://flutter-dashboard.appspot.com", - "html_url": "https://github.com/apps/godofredo-test", - "created_at": "2020-05-10T00:32:46Z", - "updated_at": "2020-05-10T00:32:46Z", - "permissions": { - "checks": "write", - "contents": "read", - "metadata": "read" - }, - "events": [ - "check_run", - "check_suite", - "label" - ] - }, - "pull_requests": [] - }, - "repository": { - "id": 212688278, - "node_id": "MDEwOlJlcG9zaXRvcnkyMTI2ODgyNzg=", - "name": "cocoon", - "full_name": "flutter/cocoon", - "private": false, - "owner": { - "login": "abc", - "id": 54371434, - "node_id": "MDQ6VXNlcjU0MzcxNDM0", - "avatar_url": "https://avatars3.githubusercontent.com/u/54371434?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/abc", - "html_url": "https://github.com/abc", - "followers_url": "https://api.github.com/users/abc/followers", - "following_url": "https://api.github.com/users/abc/following{/other_user}", - "gists_url": "https://api.github.com/users/abc/gists{/gist_id}", - "starred_url": "https://api.github.com/users/abc/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/abc/subscriptions", - "organizations_url": "https://api.github.com/users/abc/orgs", - "repos_url": "https://api.github.com/users/abc/repos", - "events_url": "https://api.github.com/users/abc/events{/privacy}", - "received_events_url": "https://api.github.com/users/abc/received_events", - "type": "User", - "site_admin": false - }, - "html_url": "https://github.com/flutter/cocoon", - "description": "Flutter's build coordinator and aggregator", - "fork": true, - "url": "https://api.github.com/repos/flutter/cocoon", - "forks_url": "https://api.github.com/repos/flutter/cocoon/forks", - "keys_url": "https://api.github.com/repos/flutter/cocoon/keys{/key_id}", - "collaborators_url": "https://api.github.com/repos/flutter/cocoon/collaborators{/collaborator}", - "teams_url": "https://api.github.com/repos/flutter/cocoon/teams", - "hooks_url": "https://api.github.com/repos/flutter/cocoon/hooks", - "issue_events_url": "https://api.github.com/repos/flutter/cocoon/issues/events{/number}", - "events_url": "https://api.github.com/repos/flutter/cocoon/events", - "assignees_url": "https://api.github.com/repos/flutter/cocoon/assignees{/user}", - "branches_url": "https://api.github.com/repos/flutter/cocoon/branches{/branch}", - "tags_url": "https://api.github.com/repos/flutter/cocoon/tags", - "blobs_url": "https://api.github.com/repos/flutter/cocoon/git/blobs{/sha}", - "git_tags_url": "https://api.github.com/repos/flutter/cocoon/git/tags{/sha}", - "git_refs_url": "https://api.github.com/repos/flutter/cocoon/git/refs{/sha}", - "trees_url": "https://api.github.com/repos/flutter/cocoon/git/trees{/sha}", - "statuses_url": "https://api.github.com/repos/flutter/cocoon/statuses/{sha}", - "languages_url": "https://api.github.com/repos/flutter/cocoon/languages", - "stargazers_url": "https://api.github.com/repos/flutter/cocoon/stargazers", - "contributors_url": "https://api.github.com/repos/flutter/cocoon/contributors", - "subscribers_url": "https://api.github.com/repos/flutter/cocoon/subscribers", - "subscription_url": "https://api.github.com/repos/flutter/cocoon/subscription", - "commits_url": "https://api.github.com/repos/flutter/cocoon/commits{/sha}", - "git_commits_url": "https://api.github.com/repos/flutter/cocoon/git/commits{/sha}", - "comments_url": "https://api.github.com/repos/flutter/cocoon/comments{/number}", - "issue_comment_url": "https://api.github.com/repos/flutter/cocoon/issues/comments{/number}", - "contents_url": "https://api.github.com/repos/flutter/cocoon/contents/{+path}", - "compare_url": "https://api.github.com/repos/flutter/cocoon/compare/{base}...{head}", - "merges_url": "https://api.github.com/repos/flutter/cocoon/merges", - "archive_url": "https://api.github.com/repos/flutter/cocoon/{archive_format}{/ref}", - "downloads_url": "https://api.github.com/repos/flutter/cocoon/downloads", - "issues_url": "https://api.github.com/repos/flutter/cocoon/issues{/number}", - "pulls_url": "https://api.github.com/repos/flutter/cocoon/pulls{/number}", - "milestones_url": "https://api.github.com/repos/flutter/cocoon/milestones{/number}", - "notifications_url": "https://api.github.com/repos/flutter/cocoon/notifications{?since,all,participating}", - "labels_url": "https://api.github.com/repos/flutter/cocoon/labels{/name}", - "releases_url": "https://api.github.com/repos/flutter/cocoon/releases{/id}", - "deployments_url": "https://api.github.com/repos/flutter/cocoon/deployments", - "created_at": "2019-10-03T21:57:12Z", - "updated_at": "2019-10-03T21:57:14Z", - "pushed_at": "2020-05-09T23:42:23Z", - "git_url": "git://github.com/flutter/cocoon.git", - "ssh_url": "git@github.com:flutter/cocoon.git", - "clone_url": "https://github.com/flutter/cocoon.git", - "svn_url": "https://github.com/flutter/cocoon", - "homepage": null, - "size": 2941, - "stargazers_count": 0, - "watchers_count": 0, - "language": null, - "has_issues": false, - "has_projects": true, - "has_downloads": true, - "has_wiki": false, - "has_pages": false, - "forks_count": 0, - "mirror_url": null, - "archived": false, - "disabled": false, - "open_issues_count": 1, - "license": { - "key": "", - "name": "", - "spdx_id": "", - "url": "", - "node_id": "MDc6TGljZW5zZTU=" - }, - "forks": 0, - "open_issues": 1, - "watchers": 0, - "default_branch": "main" - }, - "sender": { - "login": "abc", - "id": 54371434, - "node_id": "MDQ6VXNlcjU0MzcxNDM0", - "avatar_url": "https://avatars3.githubusercontent.com/u/54371434?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/abc", - "html_url": "https://github.com/abc", - "followers_url": "https://api.github.com/users/abc/followers", - "following_url": "https://api.github.com/users/abc/following{/other_user}", - "gists_url": "https://api.github.com/users/abc/gists{/gist_id}", - "starred_url": "https://api.github.com/users/abc/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/abc/subscriptions", - "organizations_url": "https://api.github.com/users/abc/orgs", - "repos_url": "https://api.github.com/users/abc/repos", - "events_url": "https://api.github.com/users/abc/events{/privacy}", - "received_events_url": "https://api.github.com/users/abc/received_events", - "type": "User", - "site_admin": false - }, - "installation": { - "id": 8770981, - "node_id": "MDIzOkludGVncmF0aW9uSW5zdGFsbGF0aW9uODc3MDk4MQ==" - } -} -'''; diff --git a/app_dart/test/model/google/token_info_test.dart b/app_dart/test/model/google/token_info_test.dart deleted file mode 100644 index 66995e3c9..000000000 --- a/app_dart/test/model/google/token_info_test.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 'package:cocoon_service/src/model/google/token_info.dart'; -import 'package:test/test.dart'; - -void main() { - group('TokenInfo', () { - test('deserialize', () { - final TokenInfo token = TokenInfo.fromJson({ - 'iss': 'issuer', - 'azp': 'authorizedParty', - 'aud': 'audience', - 'sub': 'subject', - 'hd': 'hostedDomain', - 'email': 'test@flutter.dev', - 'email_verified': 'true', - 'at_hash': 'accessTokenHash', - 'name': 'Test Flutter', - 'picture': 'http://example.org/123.jpg', - 'given_name': 'Test', - 'family_name': 'Flutter', - 'locale': 'en', - 'iat': '12345', - 'exp': '67890', - 'jti': 'jwtId', - 'alg': 'RSA', - 'kid': 'keyId', - 'typ': 'JWT', - }); - expect(token.issuer, 'issuer'); - expect(token.authorizedParty, 'authorizedParty'); - expect(token.audience, 'audience'); - expect(token.subject, 'subject'); - expect(token.hostedDomain, 'hostedDomain'); - expect(token.email, 'test@flutter.dev'); - expect(token.emailIsVerified, isTrue); - expect(token.accessTokenHash, 'accessTokenHash'); - expect(token.fullName, 'Test Flutter'); - expect(token.profilePictureUrl, 'http://example.org/123.jpg'); - expect(token.givenName, 'Test'); - expect(token.familyName, 'Flutter'); - expect(token.locale, 'en'); - expect(token.issued, DateTime.fromMillisecondsSinceEpoch(12345 * 1000)); - expect(token.expiration, DateTime.fromMillisecondsSinceEpoch(67890 * 1000)); - expect(token.jwtId, 'jwtId'); - expect(token.algorithm, 'RSA'); - expect(token.keyId, 'keyId'); - expect(token.encoding, 'JWT'); - }); - }); -} diff --git a/app_dart/test/model/model_test.dart b/app_dart/test/model/model_test.dart deleted file mode 100644 index 53782b582..000000000 --- a/app_dart/test/model/model_test.dart +++ /dev/null @@ -1,132 +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:mirrors'; - -import 'package:cocoon_service/cocoon_service.dart'; -import 'package:gcloud/db.dart'; -import 'package:test/test.dart'; - -// Statically reference something from the Cocoon library to keep the analyzer -// happy that we're importing it (which we're doing so the mirrors system sees -// the library). -const Type libraryReference = Config; - -bool isKind(InstanceMirror annotation) => annotation.reflectee.runtimeType == Kind; -bool isProperty(InstanceMirror annotation) => SymbolName(annotation.type.simpleName).toString().endsWith('Property'); - -const Map propertyAnnotationsTypeToFieldType = { - #BlobProperty: #List, - #BoolProperty: #bool, - #DateTimeProperty: #Date, - #DoubleProperty: #double, - #IntProperty: #int, - #ListProperty: #List, - #ModelKeyProperty: #Key, - #StringListProperty: #List, - #StringProperty: #String, -}; - -void main() { - final Iterable libraries = currentMirrorSystem() - .libraries - .entries - .where((MapEntry entry) => entry.key.path.contains('cocoon_service')) - .map((MapEntry entry) => entry.value); - for (LibraryMirror library in libraries) { - final Iterable classes = library.declarations.values - .whereType() - .where((ClassMirror declaration) => declaration.hasReflectedType) - .where((ClassMirror declaration) => declaration.metadata.any(isKind)); - for (ClassMirror modelClass in classes) { - group('${modelClass.reflectedType}', () { - test('extends Model', () { - final bool isStringModel = modelClass.superclass!.reflectedType.toString() == 'Model'; - final bool isIntModel = modelClass.superclass!.reflectedType.toString() == 'Model'; - expect(isStringModel || isIntModel, isTrue); - }); - - final Iterable propertyVariables = modelClass.declarations.values - .whereType() - .where((DeclarationMirror declaration) => declaration.metadata.any(isProperty)); - - for (VariableMirror variable in propertyVariables) { - final Iterable propertyAnnotations = variable.metadata.where(isProperty); - - group(SymbolName(variable.simpleName), () { - test('contains only one property annotation', () { - expect(propertyAnnotations, hasLength(1)); - }); - - test('type matches property annotation type', () { - final Symbol propertyType = variable.type.simpleName; - expect(propertyAnnotationsTypeToFieldType[propertyAnnotations.single.type.simpleName], propertyType); - }); - - test('is not static', () { - expect(variable.isStatic, isFalse); - }); - - test('is not final', () { - expect(variable.isFinal, isFalse); - }); - }); - } - - final Iterable propertyGetters = modelClass.declarations.values - .whereType() - .where((MethodMirror method) => method.isGetter) - .where((DeclarationMirror declaration) => declaration.metadata.any(isProperty)); - - for (MethodMirror getter in propertyGetters) { - final Iterable propertyAnnotations = getter.metadata.where(isProperty); - - group(SymbolName(getter.simpleName), () { - test('contains only one property annotation', () { - expect(propertyAnnotations, hasLength(1)); - }); - - test('type matches property annotation type', () { - final Symbol propertyType = getter.returnType.simpleName; - expect(propertyAnnotationsTypeToFieldType[propertyAnnotations.single.type.simpleName], propertyType); - }); - - test('is not static', () { - expect(getter.isStatic, isFalse); - }); - - test('has corresponding setter', () { - final Iterable setter = modelClass.declarations.values - .whereType() - .where((MethodMirror method) => method.isSetter) - .where((MethodMirror setter) => setter.simpleName == SymbolName(getter.simpleName).asSetter); - expect(setter, hasLength(1)); - }); - }); - } - }); - } - } - - test('SymbolName toString', () { - expect(const SymbolName(#Foo).toString(), 'Foo'); - }); -} - -class SymbolName { - const SymbolName(this.symbol); - - final Symbol symbol; - - static final RegExp symbolToString = RegExp(r'^Symbol\("(.*)"\)$'); - - Symbol get asSetter => Symbol('$this='); - - @override - String toString() { - final String raw = symbol.toString(); - final RegExpMatch? match = symbolToString.firstMatch(raw); - return match == null ? raw : match.group(1)!; - } -} diff --git a/app_dart/test/model/push_message_test.dart b/app_dart/test/model/push_message_test.dart deleted file mode 100644 index 7a16a0df4..000000000 --- a/app_dart/test/model/push_message_test.dart +++ /dev/null @@ -1,66 +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/luci/push_message.dart'; - -import 'package:test/test.dart'; - -void main() { - test('BuildPushMessage.fromJson', () { - final BuildPushMessage data = BuildPushMessage.fromJson( - json.decode(buildPushMessageJson) as Map, - ); - - // TODO(chillers): This fails in EMEA timezones. Prefer using UTC instead of local. - expect(data.build!.createdTimestamp!.year, 2019); - expect(data.build!.createdTimestamp!.month, 8); - expect(data.build!.createdTimestamp!.day, 5); - - expect(data.build!.tags!.length, 11); - expect(data.build!.tagsByName('builder').single, 'Linux Coverage'); - expect(data.build!.tagsByName('buildset'), const [ - 'pr/git/37647', - 'sha/git/0d78fc94f890a64af140ce0a2671ac5fc636f59b', - ]); - }); -} - -const String buildPushMessageJson = '''{ - "build": { - "bucket": "luci.flutter.prod", - "canary": false, - "canary_preference": "PROD", - "created_by": "user:dnfield@google.com", - "created_ts": "1565049186247524", - "experimental": true, - "id": "8905920700440101120", - "parameters_json": "{\\"builder_name\\": \\"Linux Coverage\\", \\"properties\\": {\\"git_ref\\": \\"refs/pull/37647/head\\", \\"git_url\\": \\"https://github.com/flutter/flutter\\"}}", - "project": "flutter", - "result_details_json": "{\\"properties\\": {}, \\"swarming\\": {\\"bot_dimensions\\": {\\"caches\\": [\\"flutter_openjdk_install\\", \\"git\\", \\"goma_v2\\", \\"vpython\\"], \\"cores\\": [\\"8\\"], \\"cpu\\": [\\"x86\\", \\"x86-64\\", \\"x86-64-Broadwell_GCE\\", \\"x86-64-avx2\\"], \\"gce\\": [\\"1\\"], \\"gpu\\": [\\"none\\"], \\"id\\": [\\"luci-flutter-prod-xenial-2-bnrz\\"], \\"image\\": [\\"chrome-xenial-19052201-9cb74617499\\"], \\"inside_docker\\": [\\"0\\"], \\"kvm\\": [\\"1\\"], \\"locale\\": [\\"en_US.UTF-8\\"], \\"machine_type\\": [\\"n1-standard-8\\"], \\"os\\": [\\"Linux\\", \\"Ubuntu\\", \\"Ubuntu-16.04\\"], \\"pool\\": [\\"luci.flutter.prod\\"], \\"python\\": [\\"2.7.12\\"], \\"server_version\\": [\\"4382-5929880\\"], \\"ssd\\": [\\"0\\"], \\"zone\\": [\\"us\\", \\"us-central\\", \\"us-central1\\", \\"us-central1-c\\"]}}}", - "service_account": "flutter-prod-builder@chops-service-accounts.iam.gserviceaccount.com", - "started_ts": "1565049193786080", - "status": "STARTED", - "status_changed_ts": "1565049194386647", - "tags": [ - "build_address:luci.flutter.prod/Linux Coverage/1698", - "builder:Linux Coverage", - "buildset:pr/git/37647", - "buildset:sha/git/0d78fc94f890a64af140ce0a2671ac5fc636f59b", - "swarming_hostname:chromium-swarm.appspot.com", - "swarming_tag:log_location:logdog://logs.chromium.org/flutter/buildbucket/cr-buildbucket.appspot.com/8905920700440101120/+/annotations", - "swarming_tag:luci_project:flutter", - "swarming_tag:os:Linux", - "swarming_tag:recipe_name:flutter/flutter", - "swarming_tag:recipe_package:infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build", - "swarming_task_id:467d04f2f022d510" - ], - "updated_ts": "1565049194391321", - "url": "https://ci.chromium.org/b/8905920700440101120", - "utcnow_ts": "1565049194653640" - }, - "hostname": "cr-buildbucket.appspot.com", - "user_data": "" -}'''; diff --git a/app_dart/test/model/stage_test.dart b/app_dart/test/model/stage_test.dart deleted file mode 100644 index edd592d06..000000000 --- a/app_dart/test/model/stage_test.dart +++ /dev/null @@ -1,173 +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/model/appengine/stage.dart'; -import 'package:cocoon_service/src/model/appengine/task.dart'; -import 'package:test/test.dart'; - -import '../src/utilities/entity_generators.dart'; - -Stage buildStage({ - String name = 'stage', - List statuses = const [Task.statusNew], -}) { - final Iterable tasks = statuses.map((String status) => generateTask(1, status: status)); - final StageBuilder builder = StageBuilder() - ..name = name - ..commit = generateCommit(1) - ..tasks.addAll(tasks); - return builder.build(); -} - -void main() { - group('Stage', () { - test('ordering', () { - final List stages = [ - buildStage(name: 'devicelab'), - buildStage(name: 'unknown'), - ]; - stages.sort(); - expect(stages.map((Stage stage) => stage.name), ['devicelab', 'unknown']); - }); - - test('isManagedByDeviceLab', () { - expect(buildStage(name: 'devicelab').isManagedByDeviceLab, isTrue); - expect(buildStage(name: 'unknown').isManagedByDeviceLab, isFalse); - }); - - test('taskStatus', () { - expect( - buildStage( - statuses: [ - Task.statusSucceeded, - ], - ).taskStatus, - Task.statusSucceeded, - ); - expect( - buildStage( - statuses: [ - Task.statusSucceeded, - Task.statusSucceeded, - Task.statusFailed, - ], - ).taskStatus, - Task.statusFailed, - ); - expect( - buildStage( - statuses: [ - Task.statusNew, - Task.statusFailed, - Task.statusNew, - ], - ).taskStatus, - Task.statusFailed, - ); - expect( - buildStage( - statuses: [ - Task.statusInProgress, - Task.statusFailed, - Task.statusInProgress, - ], - ).taskStatus, - Task.statusFailed, - ); - expect( - buildStage( - statuses: [ - Task.statusSucceeded, - Task.statusFailed, - Task.statusSucceeded, - ], - ).taskStatus, - Task.statusFailed, - ); - expect( - buildStage( - statuses: [ - Task.statusNew, - Task.statusFailed, - Task.statusInProgress, - Task.statusSucceeded, - ], - ).taskStatus, - Task.statusFailed, - ); - expect( - buildStage( - statuses: [ - Task.statusNew, - Task.statusInProgress, - Task.statusNew, - ], - ).taskStatus, - Task.statusInProgress, - ); - expect( - buildStage( - statuses: [ - Task.statusNew, - Task.statusSucceeded, - Task.statusNew, - ], - ).taskStatus, - Task.statusInProgress, - ); - expect( - buildStage( - statuses: [ - Task.statusSucceeded, - Task.statusSucceeded, - Task.statusInProgress, - ], - ).taskStatus, - Task.statusInProgress, - ); - expect( - buildStage( - statuses: [ - Task.statusNew, - Task.statusNew, - ], - ).taskStatus, - Task.statusNew, - ); - expect( - buildStage( - statuses: [ - Task.statusInProgress, - Task.statusInProgress, - ], - ).taskStatus, - Task.statusInProgress, - ); - expect( - buildStage( - statuses: [ - Task.statusSucceeded, - Task.statusSucceeded, - ], - ).taskStatus, - Task.statusSucceeded, - ); - }); - }); - - group('StatusBuilder', () { - test('validates state of the stage', () { - expect(() => StageBuilder().build(), throwsStateError); - expect(() => (StageBuilder()..name = 'name').build(), throwsStateError); - expect(() => (StageBuilder()..commit = generateCommit(1)).build(), throwsStateError); - expect( - () => (StageBuilder() - ..name = 'name' - ..commit = generateCommit(1)) - .build(), - throwsStateError, - ); - }); - }); -} diff --git a/app_dart/test/model/task_test.dart b/app_dart/test/model/task_test.dart deleted file mode 100644 index e31bc6729..000000000 --- a/app_dart/test/model/task_test.dart +++ /dev/null @@ -1,354 +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/model/appengine/commit.dart'; -import 'package:cocoon_service/src/model/appengine/task.dart'; -import 'package:cocoon_service/src/model/luci/buildbucket.dart'; -import 'package:cocoon_service/src/model/luci/push_message.dart' as pm; -import 'package:cocoon_service/src/request_handling/exceptions.dart'; -import 'package:cocoon_service/src/service/datastore.dart'; -import 'package:gcloud/db.dart'; -import 'package:test/test.dart'; - -import 'package:cocoon_service/src/model/luci/buildbucket.dart' as bb; - -import '../src/datastore/fake_config.dart'; -import '../src/utilities/entity_generators.dart'; - -void main() { - group('Task', () { - test('byAttempts comparator', () { - final List tasks = [ - generateTask(1, attempts: 5), - generateTask(2, attempts: 9), - generateTask(3, attempts: 3), - ]; - tasks.sort(Task.byAttempts); - expect(tasks.map((Task task) => task.attempts!), [3, 5, 9]); - }); - - test('disallows illegal status', () { - expect(() => generateTask(1, status: 'unknown'), throwsArgumentError); - expect(() => generateTask(1)..status = 'unknown', throwsArgumentError); - }); - - group('updateFromBuild', () { - test('updates if buildNumber is null', () { - final DateTime created = DateTime.utc(2022, 1, 11, 1, 1); - final DateTime started = DateTime.utc(2022, 1, 11, 1, 2); - final DateTime completed = DateTime.utc(2022, 1, 11, 1, 3); - final pm.Build build = generatePushMessageBuild( - 1, - createdTimestamp: created, - startedTimestamp: started, - completedTimestamp: completed, - ); - final Task task = generateTask(1); - - expect(task.status, Task.statusNew); - expect(task.buildNumberList, isNull); - expect(task.buildNumber, isNull); - expect(task.endTimestamp, 0); - expect(task.createTimestamp, 0); - expect(task.startTimestamp, 0); - - task.updateFromBuild(build); - - expect(task.status, Task.statusSucceeded); - expect(task.buildNumber, 1); - expect(task.buildNumberList, '1'); - expect(task.createTimestamp, created.millisecondsSinceEpoch); - expect(task.startTimestamp, started.millisecondsSinceEpoch); - expect(task.endTimestamp, completed.millisecondsSinceEpoch); - }); - - test('defaults timestamps to 0', () { - final pm.Build build = generatePushMessageBuild(1); - final Task task = generateTask(1); - - expect(task.endTimestamp, 0); - expect(task.createTimestamp, 0); - expect(task.startTimestamp, 0); - - task.updateFromBuild(build); - - expect(task.endTimestamp, 0); - expect(task.createTimestamp, 0); - expect(task.startTimestamp, 0); - }); - - test('updates if buildNumber is prior to pushMessage', () { - final pm.Build build = generatePushMessageBuild( - 1, - buildNumber: 2, - status: pm.Status.completed, - result: pm.Result.success, - ); - final Task task = generateTask( - 1, - buildNumber: 1, - status: Task.statusInProgress, - ); - - expect(task.buildNumberList, '1'); - expect(task.status, Task.statusInProgress); - - task.updateFromBuild(build); - - expect(task.buildNumber, 2); - expect(task.buildNumberList, '1,2'); - expect(task.status, Task.statusSucceeded); - }); - - test('does not duplicate build numbers on multiple messages', () { - final pm.Build build = generatePushMessageBuild( - 1, - status: pm.Status.started, - ); - final Task task = generateTask( - 1, - buildNumber: 1, - status: Task.statusSucceeded, - ); - - expect(task.buildNumber, 1); - expect(task.buildNumberList, '1'); - expect(task.status, Task.statusSucceeded); - - task.updateFromBuild(build); - - expect(task.buildNumber, 1); - expect(task.buildNumberList, '1'); - expect(task.status, Task.statusSucceeded); - }); - - test('does not update status if older status', () { - final pm.Build build = generatePushMessageBuild( - 1, - status: pm.Status.started, - ); - final Task task = generateTask( - 1, - buildNumber: 1, - status: Task.statusSucceeded, - ); - - expect(task.buildNumber, 1); - expect(task.status, Task.statusSucceeded); - - task.updateFromBuild(build); - - expect(task.buildNumber, 1); - expect(task.status, Task.statusSucceeded); - }); - - test('does not update if build is older than task', () { - final pm.Build build = generatePushMessageBuild( - 1, - status: pm.Status.completed, - result: pm.Result.success, - ); - final Task task = generateTask( - 1, - buildNumber: 2, - status: Task.statusNew, - ); - - expect(task.buildNumber, 2); - expect(task.status, Task.statusNew); - - task.updateFromBuild(build); - - expect(task.buildNumber, 2); - expect(task.status, Task.statusNew); - }); - - test('handles cancelled build', () { - final pm.Build build = generatePushMessageBuild( - 1, - status: pm.Status.completed, - result: pm.Result.canceled, - ); - final Task task = generateTask( - 1, - buildNumber: 1, - status: Task.statusNew, - ); - - expect(task.status, Task.statusNew); - task.updateFromBuild(build); - expect(task.status, Task.statusCancelled); - }); - - test('handles infra failed build', () { - final pm.Build build = generatePushMessageBuild( - 1, - status: pm.Status.completed, - result: pm.Result.failure, - failureReason: pm.FailureReason.infraFailure, - ); - final Task task = generateTask( - 1, - buildNumber: 1, - status: Task.statusNew, - ); - - expect(task.status, Task.statusNew); - task.updateFromBuild(build); - expect(task.status, Task.statusInfraFailure); - }); - }); - }); - - group('updateFromBuildbucketBuild', () { - final DateTime startTime = DateTime(2023, 1, 1, 0, 0, 0); - final DateTime endTime = DateTime(2023, 1, 1, 0, 14, 23); - test('updates successfully', () { - final bb.Build fakeBuild = bb.Build( - builderId: const BuilderId(project: 'okay-project', bucket: 'good-bucket', builder: 'great-builder'), - number: 12345, - id: 'fake-build-id', - status: bb.Status.success, - startTime: startTime, - endTime: endTime, - input: const Input( - gitilesCommit: GitilesCommit( - project: 'flutter/flutter', - hash: '12341234', - ref: 'refs/heads/main', - ), - ), - ); - - final Task task = Task( - attempts: 1, - buildNumber: 1234, - buildNumberList: '1234', - builderName: 'great-builder', - commitKey: null, - createTimestamp: 10, - endTimestamp: 50, - luciBucket: 'good-bucket', - name: 'test123', - stageName: 'dart-internal', - startTimestamp: 10, - status: 'Failed', - key: null, - timeoutInMinutes: 0, - reason: '', - requiredCapabilities: [], - reservedForAgentId: '', - ); - - task.updateFromBuildbucketBuild(fakeBuild); - - final Task expectedUpdatedTask = Task( - attempts: 2, - buildNumber: 12345, - buildNumberList: '1234,12345', - builderName: 'great-builder', - commitKey: null, - createTimestamp: startTime.millisecondsSinceEpoch, - endTimestamp: endTime.millisecondsSinceEpoch, - luciBucket: 'good-bucket', - name: 'test123', - stageName: 'dart-internal', - startTimestamp: startTime.millisecondsSinceEpoch, - status: 'Succeeded', - key: null, - timeoutInMinutes: 0, - reason: '', - requiredCapabilities: [], - reservedForAgentId: '', - ); - - expect(task.toString(), equals(expectedUpdatedTask.toString())); - }); - }); - - // TODO(chillers): There is a bug where `dart test` does not work in offline mode. - // Need to file issue and get traces. - group('Task.fromDatastore', () { - late FakeConfig config; - late Commit commit; - late Task expectedTask; - - setUp(() { - config = FakeConfig(); - commit = generateCommit(1); - expectedTask = generateTask(1, parent: commit); - config.db.values[commit.key] = commit; - config.db.values[expectedTask.key] = expectedTask; - }); - - test('look up by id', () async { - final Task task = await Task.fromDatastore( - datastore: DatastoreService(config.db, 5), - commitKey: commit.key, - id: '${expectedTask.id}', - ); - expect(task, expectedTask); - }); - - test('look up by id fails if cannot be found', () async { - expect( - Task.fromDatastore( - datastore: DatastoreService(config.db, 5), - commitKey: commit.key, - id: '12345', - ), - throwsA(isA()), - ); - }); - - test('look up by name', () async { - final Task task = await Task.fromDatastore( - datastore: DatastoreService(config.db, 5), - commitKey: commit.key, - name: expectedTask.name, - ); - expect(task, expectedTask); - }); - - test('look up by name fails if cannot be found', () async { - try { - await Task.fromDatastore( - datastore: DatastoreService(config.db, 5), - commitKey: commit.key, - name: 'Linux not_found', - ); - } catch (e) { - expect(e, isA()); - expect( - e.toString(), - equals( - 'HTTP 500: Expected to find 1 task for Linux not_found, but found 0', - ), - ); - } - }); - - test('look up by name fails if multiple Tasks with the same name are found', () async { - final DatastoreService datastore = DatastoreService(config.db, 5); - final String taskName = expectedTask.name!; - final Task duplicatedTask = generateTask(2, parent: commit, name: taskName); - config.db.values[duplicatedTask.key] = duplicatedTask; - try { - await Task.fromDatastore( - datastore: datastore, - commitKey: commit.key, - name: taskName, - ); - } catch (e) { - expect(e, isA()); - expect( - e.toString(), - equals( - 'HTTP 500: Expected to find 1 task for $taskName, but found 2', - ), - ); - } - }); - }); -} diff --git a/app_dart/test/request_handlers/check_flaky_builders_test.dart b/app_dart/test/request_handlers/check_flaky_builders_test.dart deleted file mode 100644 index 9c5357fdd..000000000 --- a/app_dart/test/request_handlers/check_flaky_builders_test.dart +++ /dev/null @@ -1,670 +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/ci_yaml.dart'; -import 'package:cocoon_service/cocoon_service.dart'; -import 'package:cocoon_service/src/request_handlers/flaky_handler_utils.dart'; -import 'package:cocoon_service/src/service/bigquery.dart'; -import 'package:cocoon_service/src/service/github_service.dart'; -import 'package:github/github.dart'; -import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; -import 'package:yaml/yaml.dart'; -import 'package:cocoon_service/src/model/proto/internal/scheduler.pb.dart' as pb; - -import '../src/datastore/fake_config.dart'; -import '../src/request_handling/api_request_handler_tester.dart'; -import '../src/request_handling/fake_authentication.dart'; -import '../src/request_handling/fake_http.dart'; -import '../src/utilities/mocks.dart'; - -import 'check_flaky_builders_test_data.dart'; - -const String kThreshold = '0.02'; -const String kCurrentMasterSHA = 'b6156fc8d1c6e992fe4ea0b9128f9aef10443bdb'; -const String kCurrentUserName = 'Name'; -const String kCurrentUserLogin = 'login'; -const String kCurrentUserEmail = 'login@email.com'; - -class MockYaml extends Mock implements CiYaml {} - -void main() { - group('Deflake', () { - late CheckFlakyBuilders handler; - late ApiRequestHandlerTester tester; - FakeHttpRequest request; - late FakeConfig config; - FakeClientContext clientContext; - FakeAuthenticationProvider auth; - late MockBigqueryService mockBigqueryService; - MockGitHub mockGitHubClient; - late MockRepositoriesService mockRepositoriesService; - late MockPullRequestsService mockPullRequestsService; - late MockIssuesService mockIssuesService; - late MockGitService mockGitService; - MockUsersService mockUsersService; - - setUp(() { - request = FakeHttpRequest( - queryParametersValue: { - FileFlakyIssueAndPR.kThresholdKey: kThreshold, - }, - ); - - clientContext = FakeClientContext(); - auth = FakeAuthenticationProvider(clientContext: clientContext); - mockBigqueryService = MockBigqueryService(); - mockGitHubClient = MockGitHub(); - mockRepositoriesService = MockRepositoriesService(); - mockIssuesService = MockIssuesService(); - mockPullRequestsService = MockPullRequestsService(); - mockGitService = MockGitService(); - mockUsersService = MockUsersService(); - // when gets the content of .ci.yaml - when( - mockRepositoriesService.getContents( - captureAny, - kCiYamlPath, - ), - ).thenAnswer((Invocation invocation) { - return Future.value( - RepositoryContents(file: GitHubFile(content: gitHubEncode(ciYamlContent))), - ); - }); - // when gets the content of TESTOWNERS - when( - mockRepositoriesService.getContents( - captureAny, - kTestOwnerPath, - ), - ).thenAnswer((Invocation invocation) { - return Future.value( - RepositoryContents(file: GitHubFile(content: gitHubEncode(testOwnersContent))), - ); - }); - // when gets existing marks flaky prs. - when(mockPullRequestsService.list(captureAny)).thenAnswer((Invocation invocation) { - return const Stream.empty(); - }); - // when gets the current head of master branch - when(mockGitService.getReference(captureAny, kMasterRefs)).thenAnswer((Invocation invocation) { - return Future.value( - GitReference(ref: 'refs/$kMasterRefs', object: GitObject('', kCurrentMasterSHA, '')), - ); - }); - // when gets the current user. - when(mockUsersService.getCurrentUser()).thenAnswer((Invocation invocation) { - final CurrentUser result = CurrentUser(); - result.email = kCurrentUserEmail; - result.name = kCurrentUserName; - result.login = kCurrentUserLogin; - return Future.value(result); - }); - // when assigns pull request reviewer. - when( - mockGitHubClient.postJSON, PullRequest>( - captureAny, - statusCode: captureAnyNamed('statusCode'), - fail: captureAnyNamed('fail'), - headers: captureAnyNamed('headers'), - params: captureAnyNamed('params'), - convert: captureAnyNamed('convert'), - body: captureAnyNamed('body'), - preview: captureAnyNamed('preview'), - ), - ).thenAnswer((Invocation invocation) { - return Future.value(PullRequest()); - }); - when(mockGitHubClient.repositories).thenReturn(mockRepositoriesService); - when(mockGitHubClient.issues).thenReturn(mockIssuesService); - when(mockGitHubClient.pullRequests).thenReturn(mockPullRequestsService); - when(mockGitHubClient.git).thenReturn(mockGitService); - when(mockGitHubClient.users).thenReturn(mockUsersService); - config = FakeConfig( - githubService: GithubService(mockGitHubClient), - bigqueryService: mockBigqueryService, - ); - tester = ApiRequestHandlerTester(request: request); - - handler = CheckFlakyBuilders( - config: config, - authenticationProvider: auth, - ); - }); - - test('Can create pr if the flaky test is no longer flaky with a closed issue', () async { - // When queries flaky data from BigQuery. - when( - mockBigqueryService.listRecentBuildRecordsForBuilder( - kBigQueryProjectId, - builder: captureAnyNamed('builder'), - limit: captureAnyNamed('limit'), - ), - ).thenAnswer((Invocation invocation) { - return Future>.value(semanticsIntegrationTestRecordsAllPassed); - }); - // When queries flaky data from BigQuery. - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging')) - .thenAnswer((Invocation invocation) { - return Future>.value(stagingSemanticsIntegrationTestResponse); - }); - // When get issue - when(mockIssuesService.get(captureAny, captureAny)).thenAnswer((_) { - return Future.value(Issue(state: 'closed', htmlUrl: existingIssueURL)); - }); - // When creates git tree - when(mockGitService.createTree(captureAny, captureAny)).thenAnswer((_) { - return Future.value(GitTree(expectedSemanticsIntegrationTestTreeSha, '', false, [])); - }); - // When creates git commit - when(mockGitService.createCommit(captureAny, captureAny)).thenAnswer((_) { - return Future.value(GitCommit(sha: expectedSemanticsIntegrationTestTreeSha)); - }); - // When creates git reference - when(mockGitService.createReference(captureAny, captureAny, captureAny)).thenAnswer((Invocation invocation) { - return Future.value(GitReference(ref: invocation.positionalArguments[1] as String?)); - }); - // When creates pr to deflake test - when(mockPullRequestsService.create(captureAny, captureAny)).thenAnswer((_) { - return Future.value(PullRequest(number: expectedSemanticsIntegrationTestPRNumber)); - }); - - CheckFlakyBuilders.kRecordNumber = semanticsIntegrationTestRecordsAllPassed.length; - final Map result = await utf8.decoder - .bind((await tester.get(handler)).serialize() as Stream>) - .transform(json.decoder) - .single as Map; - - // Verify BigQuery is called correctly. - List captured = verify( - mockBigqueryService.listRecentBuildRecordsForBuilder( - captureAny, - builder: captureAnyNamed('builder'), - limit: captureAnyNamed('limit'), - ), - ).captured; - expect(captured.length, 3); - expect(captured[0].toString(), kBigQueryProjectId); - expect(captured[1] as String?, expectedSemanticsIntegrationTestBuilderName); - expect(captured[2] as int?, CheckFlakyBuilders.kRecordNumber); - - // Verify it gets the correct issue. - captured = verify(mockIssuesService.get(captureAny, captureAny)).captured; - expect(captured.length, 2); - expect(captured[0], Config.flutterSlug); - expect(captured[1] as int?, existingIssueNumber); - - // Verify tree is created correctly. - captured = verify(mockGitService.createTree(captureAny, captureAny)).captured; - expect(captured.length, 2); - expect(captured[0].toString(), '$kCurrentUserLogin/flutter'); - expect(captured[1], isA()); - final CreateGitTree tree = captured[1] as CreateGitTree; - expect(tree.baseTree, kCurrentMasterSHA); - expect(tree.entries!.length, 1); - expect(tree.entries![0].content, expectedSemanticsIntegrationTestCiYamlContent); - expect(tree.entries![0].path, kCiYamlPath); - expect(tree.entries![0].mode, kModifyMode); - expect(tree.entries![0].type, kModifyType); - - // Verify commit is created correctly. - captured = verify(mockGitService.createCommit(captureAny, captureAny)).captured; - expect(captured.length, 2); - expect(captured[0].toString(), '$kCurrentUserLogin/flutter'); - expect(captured[1], isA()); - final CreateGitCommit commit = captured[1] as CreateGitCommit; - expect(commit.message, expectedSemanticsIntegrationTestPullRequestTitle); - expect(commit.author!.name, kCurrentUserName); - expect(commit.author!.email, kCurrentUserEmail); - expect(commit.committer!.name, kCurrentUserName); - expect(commit.committer!.email, kCurrentUserEmail); - expect(commit.tree, expectedSemanticsIntegrationTestTreeSha); - expect(commit.parents!.length, 1); - expect(commit.parents![0], kCurrentMasterSHA); - - // Verify reference is created correctly. - captured = verify(mockGitService.createReference(captureAny, captureAny, captureAny)).captured; - expect(captured.length, 3); - expect(captured[0].toString(), '$kCurrentUserLogin/flutter'); - expect(captured[2], expectedSemanticsIntegrationTestTreeSha); - final String? ref = captured[1] as String?; - - // Verify pr is created correctly. - captured = verify(mockPullRequestsService.create(captureAny, captureAny)).captured; - expect(captured.length, 2); - expect(captured[0].toString(), Config.flutterSlug.toString()); - expect(captured[1], isA()); - final CreatePullRequest pr = captured[1] as CreatePullRequest; - expect(pr.title, expectedSemanticsIntegrationTestPullRequestTitle); - expect(pr.body, expectedSemanticsIntegrationTestPullRequestBody); - expect(pr.head, '$kCurrentUserLogin:$ref'); - expect(pr.base, 'refs/$kMasterRefs'); - - expect(result['Status'], 'success'); - }); - - test('Can create pr if the flaky test is no longer flaky without an issue', () async { - // when gets the content of .ci.yaml - when( - mockRepositoriesService.getContents( - captureAny, - kCiYamlPath, - ), - ).thenAnswer((Invocation invocation) { - return Future.value( - RepositoryContents(file: GitHubFile(content: gitHubEncode(ciYamlContentNoIssue))), - ); - }); - // When queries flaky data from BigQuery. - when( - mockBigqueryService.listRecentBuildRecordsForBuilder( - kBigQueryProjectId, - builder: captureAnyNamed('builder'), - limit: captureAnyNamed('limit'), - ), - ).thenAnswer((Invocation invocation) { - return Future>.value(semanticsIntegrationTestRecordsAllPassed); - }); - // When queries flaky data from BigQuery. - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging')) - .thenAnswer((Invocation invocation) { - return Future>.value(stagingSemanticsIntegrationTestResponse); - }); - // When creates git tree - when(mockGitService.createTree(captureAny, captureAny)).thenAnswer((_) { - return Future.value(GitTree(expectedSemanticsIntegrationTestTreeSha, '', false, [])); - }); - // When creates git commit - when(mockGitService.createCommit(captureAny, captureAny)).thenAnswer((_) { - return Future.value(GitCommit(sha: expectedSemanticsIntegrationTestTreeSha)); - }); - // When creates git reference - when(mockGitService.createReference(captureAny, captureAny, captureAny)).thenAnswer((Invocation invocation) { - return Future.value(GitReference(ref: invocation.positionalArguments[1] as String?)); - }); - // When creates pr to deflake test - when(mockPullRequestsService.create(captureAny, captureAny)).thenAnswer((_) { - return Future.value(PullRequest(number: expectedSemanticsIntegrationTestPRNumber)); - }); - - CheckFlakyBuilders.kRecordNumber = semanticsIntegrationTestRecordsAllPassed.length; - final Map result = await utf8.decoder - .bind((await tester.get(handler)).serialize() as Stream>) - .transform(json.decoder) - .single as Map; - - // Verify BigQuery is called correctly. - List captured = verify( - mockBigqueryService.listRecentBuildRecordsForBuilder( - captureAny, - builder: captureAnyNamed('builder'), - limit: captureAnyNamed('limit'), - ), - ).captured; - expect(captured.length, 3); - expect(captured[0].toString(), kBigQueryProjectId); - expect(captured[1] as String?, expectedSemanticsIntegrationTestBuilderName); - expect(captured[2] as int?, CheckFlakyBuilders.kRecordNumber); - - // Verify it does not get issue. - verifyNever(mockIssuesService.get(captureAny, captureAny)); - - // Verify tree is created correctly. - captured = verify(mockGitService.createTree(captureAny, captureAny)).captured; - expect(captured.length, 2); - expect(captured[0].toString(), '$kCurrentUserLogin/flutter'); - expect(captured[1], isA()); - final CreateGitTree tree = captured[1] as CreateGitTree; - expect(tree.baseTree, kCurrentMasterSHA); - expect(tree.entries!.length, 1); - expect(tree.entries![0].content, expectedSemanticsIntegrationTestCiYamlContent); - expect(tree.entries![0].path, kCiYamlPath); - expect(tree.entries![0].mode, kModifyMode); - expect(tree.entries![0].type, kModifyType); - - // Verify commit is created correctly. - captured = verify(mockGitService.createCommit(captureAny, captureAny)).captured; - expect(captured.length, 2); - expect(captured[0].toString(), '$kCurrentUserLogin/flutter'); - expect(captured[1], isA()); - final CreateGitCommit commit = captured[1] as CreateGitCommit; - expect(commit.message, expectedSemanticsIntegrationTestPullRequestTitle); - expect(commit.author!.name, kCurrentUserName); - expect(commit.author!.email, kCurrentUserEmail); - expect(commit.committer!.name, kCurrentUserName); - expect(commit.committer!.email, kCurrentUserEmail); - expect(commit.tree, expectedSemanticsIntegrationTestTreeSha); - expect(commit.parents!.length, 1); - expect(commit.parents![0], kCurrentMasterSHA); - - // Verify reference is created correctly. - captured = verify(mockGitService.createReference(captureAny, captureAny, captureAny)).captured; - expect(captured.length, 3); - expect(captured[0].toString(), '$kCurrentUserLogin/flutter'); - expect(captured[2], expectedSemanticsIntegrationTestTreeSha); - final String? ref = captured[1] as String?; - - // Verify pr is created correctly. - captured = verify(mockPullRequestsService.create(captureAny, captureAny)).captured; - expect(captured.length, 2); - expect(captured[0].toString(), Config.flutterSlug.toString()); - expect(captured[1], isA()); - final CreatePullRequest pr = captured[1] as CreatePullRequest; - expect(pr.title, expectedSemanticsIntegrationTestPullRequestTitle); - expect(pr.body, expectedSemanticsIntegrationTestPullRequestBodyNoIssue); - expect(pr.head, '$kCurrentUserLogin:$ref'); - expect(pr.base, 'refs/$kMasterRefs'); - - expect(result['Status'], 'success'); - }); - - test('Do not create PR if the builder is in the ignored list', () async { - // when gets the content of .ci.yaml - when( - mockRepositoriesService.getContents( - captureAny, - kCiYamlPath, - ), - ).thenAnswer((Invocation invocation) { - return Future.value( - RepositoryContents(file: GitHubFile(content: gitHubEncode(ciYamlContentFlakyInIgnoreList))), - ); - }); - // When queries flaky data from BigQuery. - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging')) - .thenAnswer((Invocation invocation) { - return Future>.value(stagingSemanticsIntegrationTestResponse); - }); - CheckFlakyBuilders.kRecordNumber = semanticsIntegrationTestRecordsAllPassed.length; - final Map result = await utf8.decoder - .bind((await tester.get(handler)).serialize() as Stream>) - .transform(json.decoder) - .single as Map; - - // Verify pr is not called correctly. - verifyNever(mockPullRequestsService.create(captureAny, captureAny)).captured; - - expect(result['Status'], 'success'); - }); - - test('Do not create pr if the issue is still open', () async { - // When queries flaky data from BigQuery. - when( - mockBigqueryService.listRecentBuildRecordsForBuilder( - kBigQueryProjectId, - builder: captureAnyNamed('builder'), - limit: captureAnyNamed('limit'), - ), - ).thenAnswer((Invocation invocation) { - return Future>.value(semanticsIntegrationTestRecordsAllPassed); - }); - // When queries flaky data from BigQuery. - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging')) - .thenAnswer((Invocation invocation) { - return Future>.value(stagingSemanticsIntegrationTestResponse); - }); - // When get issue - when(mockIssuesService.get(captureAny, captureAny)).thenAnswer((_) { - return Future.value(Issue(state: 'open', htmlUrl: existingIssueURL)); - }); - CheckFlakyBuilders.kRecordNumber = semanticsIntegrationTestRecordsAllPassed.length; - final Map result = await utf8.decoder - .bind((await tester.get(handler)).serialize() as Stream>) - .transform(json.decoder) - .single as Map; - - // Verify it gets the correct issue. - final List captured = verify(mockIssuesService.get(captureAny, captureAny)).captured; - expect(captured.length, 2); - expect(captured[0], Config.flutterSlug); - expect(captured[1] as int?, existingIssueNumber); - - // Verify pr is not created. - verifyNever(mockPullRequestsService.create(captureAny, captureAny)); - - expect(result['Status'], 'success'); - }); - - test('Do not create pr and do not create issue if the records have flaky runs and there is an open issue', - () async { - // When queries flaky data from BigQuery. - when( - mockBigqueryService.listRecentBuildRecordsForBuilder( - kBigQueryProjectId, - builder: captureAnyNamed('builder'), - limit: captureAnyNamed('limit'), - ), - ).thenAnswer((Invocation invocation) { - return Future>.value(semanticsIntegrationTestRecordsFlaky); - }); - // When get issue - when(mockIssuesService.get(captureAny, captureAny)).thenAnswer((_) { - return Future.value( - Issue( - state: 'closed', - htmlUrl: existingIssueURL, - closedAt: DateTime.now().subtract(const Duration(days: kGracePeriodForClosedFlake - 1)), - ), - ); - }); - // When queries flaky data from BigQuery. - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging')) - .thenAnswer((Invocation invocation) { - return Future>.value(stagingSemanticsIntegrationTestResponse); - }); - - CheckFlakyBuilders.kRecordNumber = semanticsIntegrationTestRecordsAllPassed.length + 1; - final Map result = await utf8.decoder - .bind((await tester.get(handler)).serialize() as Stream>) - .transform(json.decoder) - .single as Map; - - // Verify pr is not created. - verifyNever(mockPullRequestsService.create(captureAny, captureAny)); - - // Verify issue is created correctly. - verifyNever(mockPullRequestsService.create(captureAny, captureAny)); - - expect(result['Status'], 'success'); - }); - - test('Do not create pr and do not create issue if the records have flaky runs and there is a recently closed issue', - () async { - // When get issue - when(mockIssuesService.get(captureAny, captureAny)).thenAnswer((_) { - return Future.value( - Issue( - state: 'open', - htmlUrl: existingIssueURL, - ), - ); - }); - // When queries flaky data from BigQuery. - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging')) - .thenAnswer((Invocation invocation) { - return Future>.value(stagingSemanticsIntegrationTestResponse); - }); - - CheckFlakyBuilders.kRecordNumber = semanticsIntegrationTestRecordsAllPassed.length + 1; - final Map result = await utf8.decoder - .bind((await tester.get(handler)).serialize() as Stream>) - .transform(json.decoder) - .single as Map; - - // Verify pr is not created. - verifyNever(mockPullRequestsService.create(captureAny, captureAny)); - - // Verify issue is created correctly. - verifyNever(mockPullRequestsService.create(captureAny, captureAny)); - - expect(result['Status'], 'success'); - }); - - test('Do not create pr if the records have failed runs', () async { - // When queries flaky data from BigQuery. - when( - mockBigqueryService.listRecentBuildRecordsForBuilder( - kBigQueryProjectId, - builder: captureAnyNamed('builder'), - limit: captureAnyNamed('limit'), - ), - ).thenAnswer((Invocation invocation) { - return Future>.value(semanticsIntegrationTestRecordsFailed); - }); - // When queries flaky data from BigQuery. - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging')) - .thenAnswer((Invocation invocation) { - return Future>.value(stagingSemanticsIntegrationTestResponse); - }); - // When get issue - when(mockIssuesService.get(captureAny, captureAny)).thenAnswer((_) { - return Future.value( - Issue( - state: 'closed', - htmlUrl: existingIssueURL, - closedAt: DateTime.now().subtract(const Duration(days: 50)), - ), - ); - }); - - CheckFlakyBuilders.kRecordNumber = semanticsIntegrationTestRecordsFailed.length; - final Map result = await utf8.decoder - .bind((await tester.get(handler)).serialize() as Stream>) - .transform(json.decoder) - .single as Map; - - // Verify BigQuery is called correctly. - List captured = verify( - mockBigqueryService.listRecentBuildRecordsForBuilder( - captureAny, - builder: captureAnyNamed('builder'), - limit: captureAnyNamed('limit'), - ), - ).captured; - expect(captured.length, 3); - expect(captured[0].toString(), kBigQueryProjectId); - expect(captured[1] as String?, expectedSemanticsIntegrationTestBuilderName); - expect(captured[2] as int?, CheckFlakyBuilders.kRecordNumber); - - // Verify it gets the correct issue. - captured = verify(mockIssuesService.get(captureAny, captureAny)).captured; - expect(captured.length, 2); - expect(captured[0], Config.flutterSlug); - expect(captured[1] as int?, existingIssueNumber); - - // Verify pr is not created. - verifyNever(mockPullRequestsService.create(captureAny, captureAny)); - - expect(result['Status'], 'success'); - }); - - test('Do not create pr if there is an open one', () async { - // When queries flaky data from BigQuery. - when( - mockBigqueryService.listRecentBuildRecordsForBuilder( - kBigQueryProjectId, - builder: captureAnyNamed('builder'), - limit: captureAnyNamed('limit'), - ), - ).thenAnswer((Invocation invocation) { - return Future>.value(semanticsIntegrationTestRecordsAllPassed); - }); - // When queries flaky data from BigQuery. - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging')) - .thenAnswer((Invocation invocation) { - return Future>.value(stagingSemanticsIntegrationTestResponse); - }); - // when gets existing marks flaky prs. - when(mockPullRequestsService.list(captureAny)).thenAnswer((Invocation invocation) { - return Stream.value(PullRequest(body: expectedSemanticsIntegrationTestPullRequestBody)); - }); - // When get issue - when(mockIssuesService.get(captureAny, captureAny)).thenAnswer((_) { - return Future.value(Issue(state: 'closed', htmlUrl: existingIssueURL)); - }); - - CheckFlakyBuilders.kRecordNumber = semanticsIntegrationTestRecordsAllPassed.length; - final Map result = await utf8.decoder - .bind((await tester.get(handler)).serialize() as Stream>) - .transform(json.decoder) - .single as Map; - - // Verify pr is not created. - verifyNever(mockPullRequestsService.create(captureAny, captureAny)); - - expect(result['Status'], 'success'); - }); - - test('Do not create pr if not enough records', () async { - // When queries flaky data from BigQuery. - when( - mockBigqueryService.listRecentBuildRecordsForBuilder( - kBigQueryProjectId, - builder: captureAnyNamed('builder'), - limit: captureAnyNamed('limit'), - ), - ).thenAnswer((Invocation invocation) { - return Future>.value(semanticsIntegrationTestRecordsAllPassed); - }); - // When queries flaky data from BigQuery. - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging')) - .thenAnswer((Invocation invocation) { - return Future>.value(stagingSemanticsIntegrationTestResponse); - }); - // When get issue - when(mockIssuesService.get(captureAny, captureAny)).thenAnswer((_) { - return Future.value( - Issue( - state: 'closed', - htmlUrl: existingIssueURL, - closedAt: DateTime.now().subtract(const Duration(days: 50)), - ), - ); - }); - - CheckFlakyBuilders.kRecordNumber = semanticsIntegrationTestRecordsAllPassed.length + 1; - final Map result = await utf8.decoder - .bind((await tester.get(handler)).serialize() as Stream>) - .transform(json.decoder) - .single as Map; - - // Verify BigQuery is called correctly. - List captured = verify( - mockBigqueryService.listRecentBuildRecordsForBuilder( - captureAny, - builder: captureAnyNamed('builder'), - limit: captureAnyNamed('limit'), - ), - ).captured; - expect(captured.length, 3); - expect(captured[0].toString(), kBigQueryProjectId); - expect(captured[1] as String?, expectedSemanticsIntegrationTestBuilderName); - expect(captured[2] as int?, CheckFlakyBuilders.kRecordNumber); - - // Verify it gets the correct issue. - captured = verify(mockIssuesService.get(captureAny, captureAny)).captured; - expect(captured.length, 2); - expect(captured[0], Config.flutterSlug); - expect(captured[1] as int?, existingIssueNumber); - - // Verify pr is not created. - verifyNever(mockPullRequestsService.create(captureAny, captureAny)); - - expect(result['Status'], 'success'); - }); - - test('getIgnoreFlakiness handles non-existing builderame', () async { - final YamlMap? ci = loadYaml(ciYamlContent) as YamlMap?; - final pb.SchedulerConfig unCheckedSchedulerConfig = pb.SchedulerConfig()..mergeFromProto3Json(ci); - final CiYaml ciYaml = CiYaml( - slug: Config.flutterSlug, - branch: Config.defaultBranch(Config.flutterSlug), - config: unCheckedSchedulerConfig, - ); - CheckFlakyBuilders.getIgnoreFlakiness('Non_existing', ciYaml); - }); - }); -} diff --git a/app_dart/test/request_handlers/check_flaky_builders_test_data.dart b/app_dart/test/request_handlers/check_flaky_builders_test_data.dart deleted file mode 100644 index 35cb0a2f1..000000000 --- a/app_dart/test/request_handlers/check_flaky_builders_test_data.dart +++ /dev/null @@ -1,300 +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/bigquery.dart'; - -const int existingIssueNumber = 85578; -const String existingIssueURL = 'https://github.com/flutter/flutter/issues/$existingIssueNumber'; - -const String ciYamlContent = ''' -# 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/master/scheduler/README.md -enabled_branches: - - master - -targets: - - name: Mac_android android_semantics_integration_test - presubmit: false - bringup: true # Flaky $existingIssueURL - scheduler: luci - properties: - tags: > - ["devicelab"] - task_name: android_semantics_integration_test - - name: Mac_android ignore_myflakiness - bringup: true - presubmit: false - scheduler: luci - properties: - ignore_flakiness: "true" - tags: > - ["devicelab"] - task_name: ignore_myflakiness - - name: Linux analyze - scheduler: luci - properties: - tags: > - ["framework","hostonly"] - - name: Windows framework_tests_misc - presubmit: false - scheduler: luci - properties: - tags: > - ["shard"] -'''; - -const String ciYamlContentNoIssue = ''' -# 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/master/scheduler/README.md -enabled_branches: - - master - -targets: - - name: Mac_android android_semantics_integration_test - presubmit: false - bringup: true - scheduler: luci - properties: - tags: > - ["devicelab"] - task_name: android_semantics_integration_test - - name: Mac_android ignore_myflakiness - bringup: true - presubmit: false - scheduler: luci - properties: - ignore_flakiness: "true" - tags: > - ["devicelab"] - task_name: ignore_myflakiness - - name: Linux analyze - scheduler: luci - properties: - tags: > - ["framework","hostonly"] - - name: Windows framework_tests_misc - presubmit: false - scheduler: luci - properties: - tags: > - ["shard"] -'''; - -const String ciYamlContentFlakyInIgnoreList = ''' -# 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/master/scheduler/README.md -enabled_branches: - - master - -targets: - - name: Mac_ios32 native_ui_tests_ios - presubmit: false - bringup: true - scheduler: luci - properties: - tags: > - ["devicelab"] - task_name: native_ui_tests_ios -'''; - -const String testOwnersContent = ''' - -# Below is a list of Flutter team members' GitHub handles who are -# test owners of this repository. -# -# These owners are mainly team leaders and their sub-teams. Please feel -# free to claim ownership by adding your handle to corresponding tests. -# -# This file will be used as a reference when new flaky bugs are filed and -# the TL will be assigned and the sub-team will be labeled by default -# for further triage. - -## Linux Android DeviceLab tests -/dev/devicelab/bin/tasks/android_semantics_integration_test.dart @HansMuller @flutter/framework - -## Host only framework tests -# Linux analyze -/dev/bots/analyze.dart @HansMuller @flutter/framework - -## Shards tests -# framework_tests @HansMuller @flutter/framework - - -/dev/devicelab/bin/tasks/android_semantics_integration_test.dart @HansMuller @flutter/framework -'''; - -const String expectedSemanticsIntegrationTestResponseBody = ''' - - -The post-submit test builder `Mac_android android_semantics_integration_test`, which has been marked `bringup: true`, had 3 flakes over past 10 commits. - -One recent flaky example for a same commit: https://ci.chromium.org/ui/p/flutter/builders/staging/Mac_android%20android_semantics_integration_test/103 -Commit: https://github.com/flutter/flutter/commit/abc - -Flaky builds: -https://ci.chromium.org/ui/p/flutter/builders/staging/Mac_android%20android_semantics_integration_test/103 -https://ci.chromium.org/ui/p/flutter/builders/staging/Mac_android%20android_semantics_integration_test/102 -https://ci.chromium.org/ui/p/flutter/builders/staging/Mac_android%20android_semantics_integration_test/101 - -Recent test runs: -https://flutter-dashboard.appspot.com/#/build?taskFilter=Mac_android%20android_semantics_integration_test - -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). -'''; - -final List semanticsIntegrationTestRecordsAllPassed = [ - BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false), - BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false), - BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false), - BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false), - BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false), - BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false), - BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false), - BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false), -]; - -final List semanticsIntegrationTestRecordsFlaky = [ - BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false), - BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false), - BuilderRecord(commit: 'abc', isFlaky: true, isFailed: false), - BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false), - BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false), - BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false), - BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false), - BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false), -]; - -final List semanticsIntegrationTestRecordsFailed = [ - BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false), - BuilderRecord(commit: 'abc', isFlaky: false, isFailed: true), - BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false), - BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false), - BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false), - BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false), - BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false), - BuilderRecord(commit: 'abc', isFlaky: false, isFailed: false), -]; - -const String expectedSemanticsIntegrationTestOwner = 'HansMuller'; -const List expectedSemanticsIntegrationTestLabels = [ - 'c: flake', - 'P0', - 'framework', -]; -const String expectedSemanticsIntegrationTestTreeSha = 'abcdefg'; -const int expectedSemanticsIntegrationTestPRNumber = 123; - -const String expectedSemanticsIntegrationTestBuilderName = 'Mac_android android_semantics_integration_test'; -const String expectedSemanticsIntegrationTestCiYamlContent = ''' -# 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/master/scheduler/README.md -enabled_branches: - - master - -targets: - - name: Mac_android android_semantics_integration_test - presubmit: false - scheduler: luci - properties: - tags: > - ["devicelab"] - task_name: android_semantics_integration_test - - name: Mac_android ignore_myflakiness - bringup: true - presubmit: false - scheduler: luci - properties: - ignore_flakiness: "true" - tags: > - ["devicelab"] - task_name: ignore_myflakiness - - name: Linux analyze - scheduler: luci - properties: - tags: > - ["framework","hostonly"] - - name: Windows framework_tests_misc - presubmit: false - scheduler: luci - properties: - tags: > - ["shard"] -'''; -const String expectedSemanticsIntegrationTestPullRequestTitle = - 'Marks Mac_android android_semantics_integration_test to be unflaky'; -const String expectedSemanticsIntegrationTestPullRequestBody = ''' - -The issue $existingIssueURL has been closed, and the test has been passing for [8 consecutive runs](https://data.corp.google.com/sites/flutter_infra_metrics_datasite/flutter_check_test_flakiness_status_dashboard/?p=BUILDER_NAME:%22Mac_android%20android_semantics_integration_test%22). -This test can be marked as unflaky. -'''; -const String expectedSemanticsIntegrationTestPullRequestBodyNoIssue = ''' - -The test has been passing for [8 consecutive runs](https://data.corp.google.com/sites/flutter_infra_metrics_datasite/flutter_check_test_flakiness_status_dashboard/?p=BUILDER_NAME:%22Mac_android%20android_semantics_integration_test%22). -This test can be marked as unflaky. -'''; - -final List stagingSemanticsIntegrationTestResponse = [ - BuilderStatistic( - name: 'Mac_android android_semantics_integration_test', - flakyRate: 0.5, - flakyBuilds: ['103', '102', '101'], - succeededBuilds: ['203', '202', '201', '200', '199', '198', '197'], - recentCommit: 'abc', - flakyBuildOfRecentCommit: '103', - flakyNumber: 3, - totalNumber: 10, - ), - // This builder is flakey, but it should be - // ignored because it has ignore_flakiness set. - BuilderStatistic( - name: 'Mac_android ignore_myflakiness', - flakyRate: 0.5, - flakyBuilds: ['103', '102', '101'], - succeededBuilds: ['203', '202', '201', '200', '199', '198', '197'], - recentCommit: 'abc', - flakyBuildOfRecentCommit: '103', - flakyNumber: 3, - totalNumber: 10, - ), -]; - -String gitHubEncode(String source) { - final List utf8Characters = utf8.encode(source); - final String base64encoded = base64Encode(utf8Characters); - return base64encoded; -} diff --git a/app_dart/test/request_handlers/create_branch_test.dart b/app_dart/test/request_handlers/create_branch_test.dart deleted file mode 100644 index 134a10962..000000000 --- a/app_dart/test/request_handlers/create_branch_test.dart +++ /dev/null @@ -1,37 +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:cocoon_service/src/request_handlers/create_branch.dart'; -import 'package:cocoon_service/src/request_handling/request_handler.dart'; -import 'package:cocoon_service/src/service/branch_service.dart'; -import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; - -import '../src/datastore/fake_config.dart'; -import '../src/request_handling/fake_authentication.dart'; -import '../src/request_handling/fake_http.dart'; -import '../src/request_handling/request_handler_tester.dart'; -import '../src/utilities/mocks.dart'; - -void main() { - group(CreateBranch, () { - test('runs', () async { - final RequestHandlerTester tester = RequestHandlerTester(); - tester.request = FakeHttpRequest( - queryParametersValue: { - CreateBranch.branchParam: 'flutter-3.7-candidate.1', - CreateBranch.engineShaParam: 'abc123', - }, - ); - final BranchService branchService = MockBranchService(); - final RequestHandler handler = CreateBranch( - branchService: branchService, - config: FakeConfig(), - authenticationProvider: FakeAuthenticationProvider(), - ); - await tester.get(handler); - verify(branchService.branchFlutterRecipes('flutter-3.7-candidate.1', 'abc123')).called(1); - }); - }); -} diff --git a/app_dart/test/request_handlers/dart_internal_subscription_test.dart b/app_dart/test/request_handlers/dart_internal_subscription_test.dart deleted file mode 100644 index fec7726d4..000000000 --- a/app_dart/test/request_handlers/dart_internal_subscription_test.dart +++ /dev/null @@ -1,308 +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/cocoon_service.dart'; -import 'package:cocoon_service/src/model/appengine/commit.dart'; -import 'package:cocoon_service/src/model/appengine/task.dart'; -import 'package:cocoon_service/src/model/luci/buildbucket.dart'; -import 'package:cocoon_service/src/model/luci/push_message.dart' as push; -import 'package:cocoon_service/src/service/datastore.dart'; -import 'package:gcloud/db.dart'; -import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; - -import '../src/datastore/fake_config.dart'; -import '../src/request_handling/fake_authentication.dart'; -import '../src/request_handling/fake_http.dart'; -import '../src/request_handling/subscription_tester.dart'; -import '../src/utilities/entity_generators.dart'; -import '../src/utilities/mocks.dart'; - -void main() { - late DartInternalSubscription handler; - late FakeConfig config; - late FakeHttpRequest request; - late MockBuildBucketClient buildBucketClient; - late SubscriptionTester tester; - late Commit commit; - final DateTime startTime = DateTime(2023, 1, 1, 0, 0, 0); - final DateTime endTime = DateTime(2023, 1, 1, 0, 14, 23); - const String project = 'dart-internal'; - const String bucket = 'flutter'; - const String builder = 'Linux packaging_release_builder'; - const int buildId = 123456; - const String fakeHash = 'HASH12345'; - const String fakeBranch = 'test-branch'; - const String fakePubsubMessage = ''' - { - "build": { - "id": "$buildId", - "builder": { - "project": "$project", - "bucket": "$bucket", - "builder": "$builder" - } - } - } - '''; - - setUp(() async { - config = FakeConfig(); - buildBucketClient = MockBuildBucketClient(); - handler = DartInternalSubscription( - cache: CacheService(inMemory: true), - config: config, - authProvider: FakeAuthenticationProvider(), - buildBucketClient: buildBucketClient, - datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5), - ); - request = FakeHttpRequest(); - - tester = SubscriptionTester( - request: request, - ); - - commit = generateCommit( - 1, - sha: fakeHash, - branch: fakeBranch, - owner: 'flutter', - repo: 'flutter', - timestamp: 0, - ); - - final Build fakeBuild = Build( - builderId: const BuilderId(project: project, bucket: bucket, builder: builder), - number: buildId, - id: 'fake-build-id', - status: Status.success, - startTime: startTime, - endTime: endTime, - input: const Input( - gitilesCommit: GitilesCommit( - project: 'flutter/flutter', - hash: fakeHash, - ref: 'refs/heads/$fakeBranch', - ), - ), - ); - when( - buildBucketClient.getBuild( - any, - buildBucketUri: 'https://cr-buildbucket.appspot.com/prpc/buildbucket.v2.Builds', - ), - ).thenAnswer((_) => Future.value(fakeBuild)); - - final List datastoreCommit = [commit]; - await config.db.commit(inserts: datastoreCommit); - }); - - test('creates a new task successfully', () async { - tester.message = const push.PushMessage(data: fakePubsubMessage); - - await tester.post(handler); - - verify( - buildBucketClient.getBuild(any), - ).called(1); - - // This is used for testing to pull the data out of the "datastore" so that - // we can verify what was saved. - late Task taskInDb; - late Commit commitInDb; - config.db.values.forEach((k, v) { - if (v is Task && v.buildNumberList == buildId.toString()) { - taskInDb = v; - } - if (v is Commit) { - commitInDb = v; - } - }); - - // Ensure the task has the correct parent and commit key - expect( - commitInDb.id, - equals(taskInDb.commitKey?.id), - ); - - expect( - commitInDb.id, - equals(taskInDb.parentKey?.id), - ); - - // Ensure the task in the db is exactly what we expect - final Task expectedTask = Task( - attempts: 1, - buildNumber: buildId, - buildNumberList: buildId.toString(), - builderName: builder, - commitKey: commitInDb.key, - createTimestamp: startTime.millisecondsSinceEpoch, - endTimestamp: endTime.millisecondsSinceEpoch, - luciBucket: bucket, - name: builder, - stageName: 'dart-internal', - startTimestamp: startTime.millisecondsSinceEpoch, - status: 'Succeeded', - key: commit.key.append(Task), - timeoutInMinutes: 0, - reason: '', - requiredCapabilities: [], - reservedForAgentId: '', - ); - - expect( - taskInDb.toString(), - equals(expectedTask.toString()), - ); - }); - - test('updates an existing task successfully', () async { - const int existingTaskId = 123; - final Task fakeTask = Task( - attempts: 1, - buildNumber: existingTaskId, - buildNumberList: existingTaskId.toString(), - builderName: builder, - commitKey: commit.key, - createTimestamp: startTime.millisecondsSinceEpoch, - endTimestamp: endTime.millisecondsSinceEpoch, - luciBucket: bucket, - name: builder, - stageName: 'dart-internal', - startTimestamp: startTime.millisecondsSinceEpoch, - status: 'Succeeded', - key: commit.key.append(Task), - timeoutInMinutes: 0, - reason: '', - requiredCapabilities: [], - reservedForAgentId: '', - ); - final List datastoreCommit = [fakeTask]; - await config.db.commit(inserts: datastoreCommit); - - tester.message = const push.PushMessage(data: fakePubsubMessage); - - await tester.post(handler); - - verify( - buildBucketClient.getBuild(any), - ).called(1); - - // This is used for testing to pull the data out of the "datastore" so that - // we can verify what was saved. - final String expectedBuilderList = '${existingTaskId.toString()},${buildId.toString()}'; - late Task taskInDb; - late Commit commitInDb; - config.db.values.forEach((k, v) { - if (v is Task && v.buildNumberList == expectedBuilderList) { - taskInDb = v; - } - if (v is Commit) { - commitInDb = v; - } - }); - - // Ensure the task has the correct parent and commit key - expect( - commitInDb.id, - equals(taskInDb.commitKey?.id), - ); - - expect( - commitInDb.id, - equals(taskInDb.parentKey?.id), - ); - - // Ensure the task in the db is exactly what we expect - final Task expectedTask = Task( - attempts: 2, - buildNumber: buildId, - buildNumberList: expectedBuilderList, - builderName: builder, - commitKey: commitInDb.key, - createTimestamp: startTime.millisecondsSinceEpoch, - endTimestamp: endTime.millisecondsSinceEpoch, - luciBucket: bucket, - name: builder, - stageName: 'dart-internal', - startTimestamp: startTime.millisecondsSinceEpoch, - status: 'Succeeded', - key: commit.key.append(Task), - timeoutInMinutes: 0, - reason: '', - requiredCapabilities: [], - reservedForAgentId: '', - ); - - expect( - taskInDb.toString(), - equals(expectedTask.toString()), - ); - }); - - test('ignores null message', () async { - tester.message = const push.PushMessage(data: null); - expect(await tester.post(handler), equals(Body.empty)); - }); - - test('ignores message with empty build data', () async { - tester.message = const push.PushMessage(data: '{}'); - expect(await tester.post(handler), equals(Body.empty)); - }); - - test('ignores message not from flutter bucket', () async { - tester.message = const push.PushMessage( - data: ''' - { - "build": { - "id": "$buildId", - "builder": { - "project": "$project", - "bucket": "dart", - "builder": "$builder" - } - } - } - ''', - ); - expect(await tester.post(handler), equals(Body.empty)); - }); - - test('ignores message not from dart-internal project', () async { - tester.message = const push.PushMessage( - data: ''' - { - "build": { - "id": "$buildId", - "builder": { - "project": "different-project", - "bucket": "$bucket", - "builder": "$builder" - } - } - } - ''', - ); - expect(await tester.post(handler), equals(Body.empty)); - }); - - test('ignores message not from an accepted builder', () async { - tester.message = const push.PushMessage( - data: ''' - { - "build": { - "id": "$buildId", - "builder": { - "project": "different-project", - "bucket": "$bucket", - "builder": "different-builder" - } - } - } - ''', - ); - expect(await tester.post(handler), equals(Body.empty)); - }); -} diff --git a/app_dart/test/request_handlers/file_flaky_issue_and_pr_test.dart b/app_dart/test/request_handlers/file_flaky_issue_and_pr_test.dart deleted file mode 100644 index f5ec497f2..000000000 --- a/app_dart/test/request_handlers/file_flaky_issue_and_pr_test.dart +++ /dev/null @@ -1,694 +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/ci_yaml.dart'; -import 'package:cocoon_service/cocoon_service.dart'; -import 'package:cocoon_service/src/request_handlers/flaky_handler_utils.dart'; -import 'package:cocoon_service/src/service/bigquery.dart'; -import 'package:cocoon_service/src/service/github_service.dart'; -import 'package:collection/collection.dart'; -import 'package:github/github.dart'; -import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; -import 'package:yaml/yaml.dart'; -import 'package:cocoon_service/src/model/proto/internal/scheduler.pb.dart' as pb; - -import '../src/datastore/fake_config.dart'; -import '../src/request_handling/api_request_handler_tester.dart'; -import '../src/request_handling/fake_authentication.dart'; -import '../src/request_handling/fake_http.dart'; -import '../src/utilities/mocks.dart'; - -import 'file_flaky_issue_and_pr_test_data.dart'; - -const String kThreshold = '0.02'; -const String kCurrentMasterSHA = 'b6156fc8d1c6e992fe4ea0b9128f9aef10443bdb'; -const String kCurrentUserName = 'Name'; -const String kCurrentUserLogin = 'login'; -const String kCurrentUserEmail = 'login@email.com'; - -void main() { - group('Check flaky', () { - late FileFlakyIssueAndPR handler; - late ApiRequestHandlerTester tester; - late FakeHttpRequest request; - late FakeConfig config; - late FakeClientContext clientContext; - late FakeAuthenticationProvider auth; - late MockBigqueryService mockBigqueryService; - late MockGitHub mockGitHubClient; - late MockRepositoriesService mockRepositoriesService; - late MockPullRequestsService mockPullRequestsService; - late MockIssuesService mockIssuesService; - late MockGitService mockGitService; - late MockUsersService mockUsersService; - - setUp(() { - request = FakeHttpRequest( - queryParametersValue: { - FileFlakyIssueAndPR.kThresholdKey: kThreshold, - }, - ); - - clientContext = FakeClientContext(); - auth = FakeAuthenticationProvider(clientContext: clientContext); - mockBigqueryService = MockBigqueryService(); - mockGitHubClient = MockGitHub(); - mockRepositoriesService = MockRepositoriesService(); - mockIssuesService = MockIssuesService(); - mockPullRequestsService = MockPullRequestsService(); - mockGitService = MockGitService(); - mockUsersService = MockUsersService(); - // when gets the content of .ci.yaml - when( - mockRepositoriesService.getContents( - captureAny, - kCiYamlPath, - ), - ).thenAnswer((Invocation invocation) { - return Future.value( - RepositoryContents(file: GitHubFile(content: gitHubEncode(ciYamlContent))), - ); - }); - // when gets the content of TESTOWNERS - when( - mockRepositoriesService.getContents( - captureAny, - kTestOwnerPath, - ), - ).thenAnswer((Invocation invocation) { - return Future.value( - RepositoryContents(file: GitHubFile(content: gitHubEncode(testOwnersContent))), - ); - }); - when(mockIssuesService.create(any, any)).thenAnswer((_) async => Issue()); - // when gets existing flaky issues. - when(mockIssuesService.listByRepo(captureAny, state: captureAnyNamed('state'), labels: captureAnyNamed('labels'))) - .thenAnswer((Invocation invocation) { - return const Stream.empty(); - }); - // when gets existing marks flaky prs. - when(mockPullRequestsService.list(captureAny)).thenAnswer((Invocation invocation) { - return const Stream.empty(); - }); - // when gets the current head of master branch - when(mockGitService.getReference(captureAny, kMasterRefs)).thenAnswer((Invocation invocation) { - return Future.value( - GitReference(ref: 'refs/$kMasterRefs', object: GitObject('', kCurrentMasterSHA, '')), - ); - }); - // when gets the current user. - when(mockUsersService.getCurrentUser()).thenAnswer((Invocation invocation) { - final CurrentUser result = CurrentUser(); - result.email = kCurrentUserEmail; - result.name = kCurrentUserName; - result.login = kCurrentUserLogin; - return Future.value(result); - }); - // when assigns pull request reviewer. - when( - mockGitHubClient.postJSON, PullRequest>( - captureAny, - statusCode: captureAnyNamed('statusCode'), - fail: captureAnyNamed('fail'), - headers: captureAnyNamed('headers'), - params: captureAnyNamed('params'), - convert: captureAnyNamed('convert'), - body: captureAnyNamed('body'), - preview: captureAnyNamed('preview'), - ), - ).thenAnswer((Invocation invocation) { - return Future.value(PullRequest()); - }); - when(mockGitHubClient.repositories).thenReturn(mockRepositoriesService); - when(mockGitHubClient.issues).thenReturn(mockIssuesService); - when(mockGitHubClient.pullRequests).thenReturn(mockPullRequestsService); - when(mockGitHubClient.git).thenReturn(mockGitService); - when(mockGitHubClient.users).thenReturn(mockUsersService); - config = FakeConfig( - githubService: GithubService(mockGitHubClient), - bigqueryService: mockBigqueryService, - githubClient: mockGitHubClient, - ); - tester = ApiRequestHandlerTester(request: request); - - handler = FileFlakyIssueAndPR( - config: config, - authenticationProvider: auth, - ); - }); - - test('Can file issue and pr for devicelab test', () async { - // When queries flaky data from BigQuery. - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) { - return Future>.value(semanticsIntegrationTestResponse); - }); - // When creates issue - when(mockIssuesService.create(captureAny, captureAny)).thenAnswer((_) { - return Future.value(Issue(htmlUrl: expectedSemanticsIntegrationTestNewIssueURL)); - }); - // Add issue labels - when(mockIssuesService.addLabelsToIssue(captureAny, captureAny, captureAny)).thenAnswer((_) { - return Future>.value([]); - }); - // When creates git tree - when(mockGitService.createTree(captureAny, captureAny)).thenAnswer((_) { - return Future.value(GitTree(expectedSemanticsIntegrationTestTreeSha, '', false, [])); - }); - // When creates git commit - when(mockGitService.createCommit(captureAny, captureAny)).thenAnswer((_) { - return Future.value(GitCommit(sha: expectedSemanticsIntegrationTestTreeSha)); - }); - // When creates git reference - when(mockGitService.createReference(captureAny, captureAny, captureAny)).thenAnswer((Invocation invocation) { - return Future.value(GitReference(ref: invocation.positionalArguments[1] as String?)); - }); - // When creates pr to mark test flaky - when(mockPullRequestsService.create(captureAny, captureAny)).thenAnswer((_) { - return Future.value(PullRequest(number: expectedSemanticsIntegrationTestPRNumber)); - }); - final Map result = await utf8.decoder - .bind((await tester.get(handler)).serialize() as Stream>) - .transform(json.decoder) - .single as Map; - - // Verify issue is created correctly. - List captured = verify(mockIssuesService.create(captureAny, captureAny)).captured; - expect(captured.length, 2); - expect(captured[0].toString(), Config.flutterSlug.toString()); - expect(captured[1], isA()); - final IssueRequest issueRequest = captured[1] as IssueRequest; - expect(issueRequest.title, expectedSemanticsIntegrationTestResponseTitle); - expect(issueRequest.body, expectedSemanticsIntegrationTestResponseBody); - expect(issueRequest.assignee, expectedSemanticsIntegrationTestResponseAssignee); - expect( - const ListEquality().equals(issueRequest.labels, expectedSemanticsIntegrationTestResponseLabels), - isTrue, - ); - - // Verify issue label is added correctly. - captured = verify(mockIssuesService.addLabelsToIssue(captureAny, captureAny, captureAny)).captured; - expect(captured.length, 3); - expect(captured[2], ['team-framework']); - - // Verify tree is created correctly. - captured = verify(mockGitService.createTree(captureAny, captureAny)).captured; - expect(captured.length, 2); - expect(captured[0].toString(), '$kCurrentUserLogin/flutter'); - expect(captured[1], isA()); - final CreateGitTree tree = captured[1] as CreateGitTree; - expect(tree.baseTree, kCurrentMasterSHA); - expect(tree.entries!.length, 1); - expect(tree.entries![0].content, expectedSemanticsIntegrationTestCiYamlContent); - expect(tree.entries![0].path, kCiYamlPath); - expect(tree.entries![0].mode, kModifyMode); - expect(tree.entries![0].type, kModifyType); - - // Verify commit is created correctly. - captured = verify(mockGitService.createCommit(captureAny, captureAny)).captured; - expect(captured.length, 2); - expect(captured[0].toString(), '$kCurrentUserLogin/flutter'); - expect(captured[1], isA()); - final CreateGitCommit commit = captured[1] as CreateGitCommit; - expect(commit.message, expectedSemanticsIntegrationTestPullRequestTitle); - expect(commit.author!.name, kCurrentUserName); - expect(commit.author!.email, kCurrentUserEmail); - expect(commit.committer!.name, kCurrentUserName); - expect(commit.committer!.email, kCurrentUserEmail); - expect(commit.tree, expectedSemanticsIntegrationTestTreeSha); - expect(commit.parents!.length, 1); - expect(commit.parents![0], kCurrentMasterSHA); - - // Verify reference is created correctly. - captured = verify(mockGitService.createReference(captureAny, captureAny, captureAny)).captured; - expect(captured.length, 3); - expect(captured[0].toString(), '$kCurrentUserLogin/flutter'); - expect(captured[2], expectedSemanticsIntegrationTestTreeSha); - final String? ref = captured[1] as String?; - - // Verify pr is created correctly. - captured = verify(mockPullRequestsService.create(captureAny, captureAny)).captured; - expect(captured.length, 2); - expect(captured[0].toString(), Config.flutterSlug.toString()); - expect(captured[1], isA()); - final CreatePullRequest pr = captured[1] as CreatePullRequest; - expect(pr.title, expectedSemanticsIntegrationTestPullRequestTitle); - expect(pr.body, expectedSemanticsIntegrationTestPullRequestBody); - expect(pr.head, '$kCurrentUserLogin:$ref'); - expect(pr.base, 'refs/$kMasterRefs'); - - expect(result['Status'], 'success'); - }); - - test('File mulitple issues and prs', () async { - // when gets the content of .ci.yaml - when( - mockRepositoriesService.getContents( - captureAny, - kCiYamlPath, - ), - ).thenAnswer((Invocation invocation) { - return Future.value( - RepositoryContents(file: GitHubFile(content: gitHubEncode(ciYamlContentTwoFlakyTargets))), - ); - }); - // When queries flaky data from BigQuery. - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) { - return Future>.value(semanticsIntegrationTestResponse); - }); - // When creates issue - when(mockIssuesService.create(captureAny, captureAny)).thenAnswer((_) { - return Future.value(Issue(htmlUrl: expectedSemanticsIntegrationTestNewIssueURL)); - }); - // Add issue labels - when(mockIssuesService.addLabelsToIssue(captureAny, captureAny, captureAny)).thenAnswer((_) { - return Future>.value([]); - }); - // When creates git tree - when(mockGitService.createTree(captureAny, captureAny)).thenAnswer((_) { - return Future.value(GitTree(expectedSemanticsIntegrationTestTreeSha, '', false, [])); - }); - // When creates git commit - when(mockGitService.createCommit(captureAny, captureAny)).thenAnswer((_) { - return Future.value(GitCommit(sha: expectedSemanticsIntegrationTestTreeSha)); - }); - // When creates git reference - when(mockGitService.createReference(captureAny, captureAny, captureAny)).thenAnswer((Invocation invocation) { - return Future.value(GitReference(ref: invocation.positionalArguments[1] as String?)); - }); - // When creates pr to mark test flaky - when(mockPullRequestsService.create(captureAny, captureAny)).thenAnswer((_) { - return Future.value(PullRequest(number: expectedSemanticsIntegrationTestPRNumber)); - }); - final Map result = await utf8.decoder - .bind((await tester.get(handler)).serialize() as Stream>) - .transform(json.decoder) - .single as Map; - expect(result['Status'], 'success'); - expect(result['NumberOfCreatedIssuesAndPRs'], 2); - }); - - test('File issues and prs up to issueAndPRLimit', () async { - // when gets the content of .ci.yaml - config = FakeConfig( - githubService: GithubService(mockGitHubClient), - bigqueryService: mockBigqueryService, - githubClient: mockGitHubClient, - issueAndPRLimitValue: 1, - ); - handler = FileFlakyIssueAndPR( - config: config, - authenticationProvider: auth, - ); - when( - mockRepositoriesService.getContents( - captureAny, - kCiYamlPath, - ), - ).thenAnswer((Invocation invocation) { - return Future.value( - RepositoryContents(file: GitHubFile(content: gitHubEncode(ciYamlContentTwoFlakyTargets))), - ); - }); - // When queries flaky data from BigQuery. - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) { - return Future>.value(semanticsIntegrationTestResponse); - }); - // When creates issue - when(mockIssuesService.create(captureAny, captureAny)).thenAnswer((_) { - return Future.value(Issue(htmlUrl: expectedSemanticsIntegrationTestNewIssueURL)); - }); - // Add issue labels - when(mockIssuesService.addLabelsToIssue(captureAny, captureAny, captureAny)).thenAnswer((_) { - return Future>.value([]); - }); - // When creates git tree - when(mockGitService.createTree(captureAny, captureAny)).thenAnswer((_) { - return Future.value(GitTree(expectedSemanticsIntegrationTestTreeSha, '', false, [])); - }); - // When creates git commit - when(mockGitService.createCommit(captureAny, captureAny)).thenAnswer((_) { - return Future.value(GitCommit(sha: expectedSemanticsIntegrationTestTreeSha)); - }); - // When creates git reference - when(mockGitService.createReference(captureAny, captureAny, captureAny)).thenAnswer((Invocation invocation) { - return Future.value(GitReference(ref: invocation.positionalArguments[1] as String?)); - }); - // When creates pr to mark test flaky - when(mockPullRequestsService.create(captureAny, captureAny)).thenAnswer((_) { - return Future.value(PullRequest(number: expectedSemanticsIntegrationTestPRNumber)); - }); - final Map result = await utf8.decoder - .bind((await tester.get(handler)).serialize() as Stream>) - .transform(json.decoder) - .single as Map; - expect(result['Status'], 'success'); - expect(result['NumberOfCreatedIssuesAndPRs'], 1); - }); - - test('Can file issue and pr for framework host-only test', () async { - // When queries flaky data from BigQuery. - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) { - return Future>.value(analyzeTestResponse); - }); - // When creates issue - when(mockIssuesService.create(captureAny, captureAny)).thenAnswer((_) { - return Future.value(Issue(htmlUrl: expectedSemanticsIntegrationTestNewIssueURL)); - }); - // Add issue labels - when(mockIssuesService.addLabelsToIssue(captureAny, captureAny, captureAny)).thenAnswer((_) { - return Future>.value([]); - }); - // When creates git tree - when(mockGitService.createTree(captureAny, captureAny)).thenAnswer((_) { - return Future.value(GitTree(expectedSemanticsIntegrationTestTreeSha, '', false, [])); - }); - // When creates git commit - when(mockGitService.createCommit(captureAny, captureAny)).thenAnswer((_) { - return Future.value(GitCommit(sha: expectedSemanticsIntegrationTestTreeSha)); - }); - // When creates git reference - when(mockGitService.createReference(captureAny, captureAny, captureAny)).thenAnswer((Invocation invocation) { - return Future.value(GitReference(ref: invocation.positionalArguments[1] as String?)); - }); - // When creates pr to mark test flaky - when(mockPullRequestsService.create(captureAny, captureAny)).thenAnswer((_) { - return Future.value(PullRequest(number: expectedSemanticsIntegrationTestPRNumber)); - }); - final Map result = await utf8.decoder - .bind((await tester.get(handler)).serialize() as Stream>) - .transform(json.decoder) - .single as Map; - - // Verify issue is created correctly. - List captured = verify(mockIssuesService.create(captureAny, captureAny)).captured; - expect(captured.length, 2); - expect(captured[0].toString(), Config.flutterSlug.toString()); - expect(captured[1], isA()); - final IssueRequest issueRequest = captured[1] as IssueRequest; - expect(issueRequest.assignee, expectedAnalyzeTestResponseAssignee); - expect(const ListEquality().equals(issueRequest.labels, expectedAnalyzeTestResponseLabels), isTrue); - - // Verify pr is created correctly. - captured = verify(mockPullRequestsService.create(captureAny, captureAny)).captured; - expect(captured.length, 2); - expect(captured[0].toString(), Config.flutterSlug.toString()); - expect(captured[1], isA()); - - expect(result['Status'], 'success'); - }); - - test('Can file issue when limited number of successfuly builds exist', () async { - // When queries flaky data from BigQuery. - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) { - return Future>.value(limitedNumberOfBuildsResponse); - }); - // When creates issue - when(mockIssuesService.create(captureAny, captureAny)).thenAnswer((_) { - return Future.value(Issue(htmlUrl: expectedSemanticsIntegrationTestNewIssueURL)); - }); - // Add issue labels - when(mockIssuesService.addLabelsToIssue(captureAny, captureAny, captureAny)).thenAnswer((_) { - return Future>.value([]); - }); - // When creates git tree - when(mockGitService.createTree(captureAny, captureAny)).thenAnswer((_) { - return Future.value(GitTree(expectedSemanticsIntegrationTestTreeSha, '', false, [])); - }); - // When creates git commit - when(mockGitService.createCommit(captureAny, captureAny)).thenAnswer((_) { - return Future.value(GitCommit(sha: expectedSemanticsIntegrationTestTreeSha)); - }); - // When creates git reference - when(mockGitService.createReference(captureAny, captureAny, captureAny)).thenAnswer((Invocation invocation) { - return Future.value(GitReference(ref: invocation.positionalArguments[1] as String?)); - }); - // When creates pr to mark test flaky - when(mockPullRequestsService.create(captureAny, captureAny)).thenAnswer((_) { - return Future.value(PullRequest(number: expectedSemanticsIntegrationTestPRNumber)); - }); - final Map result = await utf8.decoder - .bind((await tester.get(handler)).serialize() as Stream>) - .transform(json.decoder) - .single as Map; - - // Verify issue is created correctly. - final List captured = verify(mockIssuesService.create(captureAny, captureAny)).captured; - expect(captured.length, 2); - expect(captured[0].toString(), Config.flutterSlug.toString()); - expect(captured[1], isA()); - final IssueRequest issueRequest = captured[1] as IssueRequest; - expect(issueRequest.body, expectedLimitedNumberOfBuildsResponseBody); - - expect(result['Status'], 'success'); - }); - - test('Can file issue but not pr for shard test', () async { - // When queries flaky data from BigQuery. - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) { - return Future>.value(frameworkTestResponse); - }); - // When creates issue - when(mockIssuesService.create(captureAny, captureAny)).thenAnswer((_) { - return Future.value(Issue(htmlUrl: expectedSemanticsIntegrationTestNewIssueURL)); - }); - final Map result = await utf8.decoder - .bind((await tester.get(handler)).serialize() as Stream>) - .transform(json.decoder) - .single as Map; - - // Verify issue is created correctly. - final List captured = verify(mockIssuesService.create(captureAny, captureAny)).captured; - expect(captured.length, 2); - expect(captured[0].toString(), Config.flutterSlug.toString()); - expect(captured[1], isA()); - final IssueRequest issueRequest = captured[1] as IssueRequest; - expect(issueRequest.assignee, expectedFrameworkTestResponseAssignee); - expect(const ListEquality().equals(issueRequest.labels, expectedFrameworkTestResponseLabels), isTrue); - // Verify no pr is created. - verifyNever(mockPullRequestsService.create(captureAny, captureAny)); - - expect(result['Status'], 'success'); - }); - - test('Do not create issue if there is already one', () async { - // When queries flaky data from BigQuery. - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) { - return Future>.value(semanticsIntegrationTestResponse); - }); - // when gets existing flaky issues. - when(mockIssuesService.listByRepo(captureAny, state: captureAnyNamed('state'), labels: captureAnyNamed('labels'))) - .thenAnswer((Invocation invocation) { - return Stream.fromIterable([ - Issue( - title: expectedSemanticsIntegrationTestResponseTitle, - body: expectedSemanticsIntegrationTestResponseBody, - ), - ]); - }); - // When creates git tree - when(mockGitService.createTree(captureAny, captureAny)).thenAnswer((_) { - return Future.value(GitTree(expectedSemanticsIntegrationTestTreeSha, '', false, [])); - }); - // When creates git commit - when(mockGitService.createCommit(captureAny, captureAny)).thenAnswer((_) { - return Future.value(GitCommit(sha: expectedSemanticsIntegrationTestTreeSha)); - }); - // When creates git reference - when(mockGitService.createReference(captureAny, captureAny, captureAny)).thenAnswer((Invocation invocation) { - return Future.value(GitReference(ref: invocation.positionalArguments[1] as String?)); - }); - // When creates pr to mark test flaky - when(mockPullRequestsService.create(captureAny, captureAny)).thenAnswer((_) { - return Future.value(PullRequest(number: expectedSemanticsIntegrationTestPRNumber)); - }); - final Map result = await utf8.decoder - .bind((await tester.get(handler)).serialize() as Stream>) - .transform(json.decoder) - .single as Map; - // Verify no issue is created. - verifyNever(mockIssuesService.create(captureAny, captureAny)); - // Verify no pr is created. - verifyNever(mockPullRequestsService.create(captureAny, captureAny)); - expect(result['Status'], 'success'); - }); - - test('Do not create issue if there is a recently closed one', () async { - // When queries flaky data from BigQuery. - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) { - return Future>.value(semanticsIntegrationTestResponse); - }); - // when get existing flaky issues. - when(mockIssuesService.listByRepo(captureAny, state: captureAnyNamed('state'), labels: captureAnyNamed('labels'))) - .thenAnswer((Invocation invocation) { - return Stream.fromIterable([ - Issue( - title: expectedSemanticsIntegrationTestResponseTitle, - body: expectedSemanticsIntegrationTestResponseBody, - state: 'closed', - closedAt: DateTime.now().subtract(const Duration(days: kGracePeriodForClosedFlake - 1)), - ), - ]); - }); - // When creates git tree - when(mockGitService.createTree(captureAny, captureAny)).thenAnswer((_) { - return Future.value(GitTree(expectedSemanticsIntegrationTestTreeSha, '', false, [])); - }); - // When creates git commit - when(mockGitService.createCommit(captureAny, captureAny)).thenAnswer((_) { - return Future.value(GitCommit(sha: expectedSemanticsIntegrationTestTreeSha)); - }); - // When creates git reference - when(mockGitService.createReference(captureAny, captureAny, captureAny)).thenAnswer((Invocation invocation) { - return Future.value(GitReference(ref: invocation.positionalArguments[1] as String?)); - }); - // When creates pr to mark test flaky - when(mockPullRequestsService.create(captureAny, captureAny)).thenAnswer((_) { - return Future.value(PullRequest(number: expectedSemanticsIntegrationTestPRNumber)); - }); - final Map result = await utf8.decoder - .bind((await tester.get(handler)).serialize() as Stream>) - .transform(json.decoder) - .single as Map; - // Verify no issue is created. - verifyNever(mockIssuesService.create(captureAny, captureAny)); - // Verify no pr is created. - verifyNever(mockPullRequestsService.create(captureAny, captureAny)); - expect(result['Status'], 'success'); - }); - - test('Do create issue if there is a closed one outside the grace period', () async { - // When queries flaky data from BigQuery. - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) { - return Future>.value(semanticsIntegrationTestResponse); - }); - // when get existing flaky issues. - when(mockIssuesService.listByRepo(captureAny, state: captureAnyNamed('state'), labels: captureAnyNamed('labels'))) - .thenAnswer((Invocation invocation) { - return Stream.fromIterable([ - Issue( - title: expectedSemanticsIntegrationTestResponseTitle, - body: expectedSemanticsIntegrationTestResponseBody, - state: 'closed', - closedAt: DateTime.now().subtract(const Duration(days: kGracePeriodForClosedFlake + 1)), - ), - ]); - }); - // When creates git tree - when(mockGitService.createTree(captureAny, captureAny)).thenAnswer((_) { - return Future.value(GitTree(expectedSemanticsIntegrationTestTreeSha, '', false, [])); - }); - // Add issue labels - when(mockIssuesService.addLabelsToIssue(captureAny, captureAny, captureAny)).thenAnswer((_) { - return Future>.value([]); - }); - // When creates git commit - when(mockGitService.createCommit(captureAny, captureAny)).thenAnswer((_) { - return Future.value(GitCommit(sha: expectedSemanticsIntegrationTestTreeSha)); - }); - // When creates git reference - when(mockGitService.createReference(captureAny, captureAny, captureAny)).thenAnswer((Invocation invocation) { - return Future.value(GitReference(ref: invocation.positionalArguments[1] as String?)); - }); - // When creates pr to mark test flaky - when(mockPullRequestsService.create(captureAny, captureAny)).thenAnswer((_) { - return Future.value(PullRequest(number: expectedSemanticsIntegrationTestPRNumber)); - }); - final Map result = await utf8.decoder - .bind((await tester.get(handler)).serialize() as Stream>) - .transform(json.decoder) - .single as Map; - // Verify issue is created correctly. - final List captured = verify(mockIssuesService.create(captureAny, captureAny)).captured; - expect(captured.length, 2); - expect(captured[0].toString(), Config.flutterSlug.toString()); - expect(captured[1], isA()); - final IssueRequest issueRequest = captured[1] as IssueRequest; - expect(issueRequest.title, expectedSemanticsIntegrationTestResponseTitle); - expect(issueRequest.body, expectedSemanticsIntegrationTestResponseBody); - expect(issueRequest.assignee, expectedSemanticsIntegrationTestResponseAssignee); - expect( - const ListEquality().equals(issueRequest.labels, expectedSemanticsIntegrationTestResponseLabels), - isTrue, - ); - - expect(result['Status'], 'success'); - }); - - test('Do not create an issue or PR if the test is already flaky', () async { - // When queries flaky data from BigQuery. - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) { - return Future>.value(semanticsIntegrationTestResponse); - }); - // when gets the content of .ci.yaml - when( - mockRepositoriesService.getContents( - captureAny, - kCiYamlPath, - ), - ).thenAnswer((Invocation invocation) { - return Future.value( - RepositoryContents(file: GitHubFile(content: gitHubEncode(ciYamlContentAlreadyFlaky))), - ); - }); - - final Map result = await utf8.decoder - .bind((await tester.get(handler)).serialize() as Stream>) - .transform(json.decoder) - .single as Map; - // Verify no issue is created. - verifyNever(mockIssuesService.create(captureAny, captureAny)); - // Verify no pr is created. - verifyNever(mockPullRequestsService.create(captureAny, captureAny)); - - expect(result['Status'], 'success'); - }); - - test('Do not create PR if there is already an opened one', () async { - // When queries flaky data from BigQuery. - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) { - return Future>.value(semanticsIntegrationTestResponse); - }); - // when gets existing marks flaky prs. - when(mockPullRequestsService.list(captureAny)).thenAnswer((Invocation invocation) { - return Stream.fromIterable([ - PullRequest( - title: expectedSemanticsIntegrationTestPullRequestTitle, - body: expectedSemanticsIntegrationTestPullRequestBody, - state: 'open', - ), - ]); - }); - - final Map result = await utf8.decoder - .bind((await tester.get(handler)).serialize() as Stream>) - .transform(json.decoder) - .single as Map; - // Verify no pr is created. - verifyNever(mockPullRequestsService.create(captureAny, captureAny)); - - expect(result['Status'], 'success'); - }); - }); - - test('retrieveMetaTagsFromContent can work with different newlines', () async { - const String differentNewline = - ''; - final Map metaTags = retrieveMetaTagsFromContent(differentNewline)!; - expect(metaTags['name'], 'Mac_android android_semantics_integration_test'); - }); - - test('getIgnoreFlakiness handles non-existing builderame', () async { - final YamlMap? ci = loadYaml(ciYamlContent) as YamlMap?; - final pb.SchedulerConfig unCheckedSchedulerConfig = pb.SchedulerConfig()..mergeFromProto3Json(ci); - final CiYaml ciYaml = CiYaml( - slug: Config.flutterSlug, - branch: Config.defaultBranch(Config.flutterSlug), - config: unCheckedSchedulerConfig, - ); - expect(FileFlakyIssueAndPR.getIgnoreFlakiness('Non_existing', ciYaml), false); - }); -} diff --git a/app_dart/test/request_handlers/file_flaky_issue_and_pr_test_data.dart b/app_dart/test/request_handlers/file_flaky_issue_and_pr_test_data.dart deleted file mode 100644 index 5ede4c011..000000000 --- a/app_dart/test/request_handlers/file_flaky_issue_and_pr_test_data.dart +++ /dev/null @@ -1,384 +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/bigquery.dart'; - -const String ciYamlContent = ''' -# 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/master/scheduler/README.md -enabled_branches: - - master - -targets: - - name: Mac_android android_semantics_integration_test - builder: Mac_android android_semantics_integration_test - presubmit: false - scheduler: luci - properties: - tags: > - ["devicelab"] - task_name: android_semantics_integration_test - - name: Mac_android ignore_myflakiness - bringup: true - builder: Mac_android ignore_myflakiness - presubmit: false - scheduler: luci - properties: - ignore_flakiness: "true" - tags: > - ["devicelab"] - task_name: ignore_myflakiness - - name: Linux analyze - builder: Linux analyze - scheduler: luci - properties: - tags: > - ["framework","hostonly"] - - name: Windows framework_tests_misc - builder: Windows framework_tests_misc - presubmit: false - scheduler: luci - properties: - tags: > - ["shard"] -'''; - -const String ciYamlContentTwoFlakyTargets = ''' -# 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/master/scheduler/README.md -enabled_branches: - - master - -targets: - - name: Mac_android android_semantics_integration_test - builder: Mac_android android_semantics_integration_test - presubmit: false - scheduler: luci - properties: - tags: > - ["devicelab"] - task_name: android_semantics_integration_test - - name: Mac_android ignore_myflakiness - builder: Mac_android ignore_myflakiness - presubmit: false - scheduler: luci - properties: - tags: > - ["devicelab"] - task_name: ignore_myflakiness - - name: Linux analyze - builder: Linux analyze - scheduler: luci - properties: - tags: > - ["framework","hostonly"] - - name: Windows framework_tests_misc - builder: Windows framework_tests_misc - presubmit: false - scheduler: luci - properties: - tags: > - ["shard"] -'''; - -const String ciYamlContentAlreadyFlaky = ''' -# 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/master/scheduler/README.md -enabled_branches: - - master - -targets: - - name: Mac_android android_semantics_integration_test - builder: Mac_android android_semantics_integration_test - bringup: true - presubmit: false - scheduler: luci - properties: - tags: > - ["devicelab"] - task_name: android_semantics_integration_test - - name: Mac_android ignore_myflakiness - bringup: true - builder: Mac_android ignore_myflakiness - presubmit: false - scheduler: luci - properties: - ignore_flakiness: "true" - tags: > - ["devicelab"] - - name: Linux analyze - builder: Linux analyze - scheduler: luci - properties: - tags: > - ["framework","hostonly"] - - name: Windows framework_tests_misc - builder: Windows framework_tests_misc - presubmit: false - scheduler: luci - properties: - tags: > - ["shard"] -'''; - -const String testOwnersContent = ''' - -# Below is a list of Flutter team members' GitHub handles who are -# test owners of this repository. -# -# These owners are mainly team leaders and their sub-teams. Please feel -# free to claim ownership by adding your handle to corresponding tests. -# -# This file will be used as a reference when new flaky bugs are filed and -# the TL will be assigned and the sub-team will be labeled by default -# for further triage. - -## Linux Android DeviceLab tests -/dev/devicelab/bin/tasks/android_semantics_integration_test.dart @HansMuller @flutter/framework - -## Host only framework tests -# Linux analyze -/dev/bots/analyze.dart @HansMuller @flutter/framework - -## Firebase tests -/dev/integration_tests/abstract_method_smoke_test @blasten @flutter/android - -## Shards tests -# framework_tests @HansMuller @flutter/framework - -## Mac Android DeviceLab tests -/dev/devicelab/bin/tasks/android_semantics_integration_test.dart @HansMuller @flutter/framework -'''; - -const String jobNotCompleteResponse = ''' -{ - "jobComplete" : false -} -'''; - -const String expectedSemanticsIntegrationTestNewIssueURL = 'https://something.something'; -const String expectedSemanticsIntegrationTestTreeSha = 'abcdefg'; -const int expectedSemanticsIntegrationTestPRNumber = 123; - -final List semanticsIntegrationTestResponse = [ - BuilderStatistic( - name: 'Mac_android android_semantics_integration_test', - flakyRate: 0.5, - flakyBuilds: ['103', '102', '101'], - succeededBuilds: ['203', '202', '201'], - recentCommit: 'abc', - flakyBuildOfRecentCommit: '103', - flakyNumber: 3, - totalNumber: 6, - ), - // This builder is flakey, but it should be - // ignored because it has ignore_flakiness set. - BuilderStatistic( - name: 'Mac_android ignore_myflakiness', - flakyRate: 0.5, - flakyBuilds: ['103', '102', '101'], - succeededBuilds: ['203', '202', '201'], - recentCommit: 'abc', - flakyBuildOfRecentCommit: '103', - flakyNumber: 3, - totalNumber: 6, - ), -]; - -const String expectedSemanticsIntegrationTestResponseTitle = - 'Mac_android android_semantics_integration_test is 50.00% flaky'; -const String expectedSemanticsIntegrationTestResponseBody = ''' - - -The post-submit test builder `Mac_android android_semantics_integration_test` had a flaky ratio 50.00% for the past (up to) 100 commits, which is above our 2.00% threshold. - -One recent flaky example for a same commit: https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/103 -Commit: https://github.com/flutter/flutter/commit/abc - -Flaky builds: -https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/103 -https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/102 -https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/101 - -Recent test runs: -https://flutter-dashboard.appspot.com/#/build?taskFilter=Mac_android%20android_semantics_integration_test - -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). -'''; - -const String expectedSemanticsIntegrationTestResponseAssignee = 'HansMuller'; -const List expectedSemanticsIntegrationTestResponseLabels = [ - 'c: flake', - 'P0', - 'team-framework', -]; -const String expectedSemanticsIntegrationTestCiYamlContent = ''' -# 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/master/scheduler/README.md -enabled_branches: - - master - -targets: - - name: Mac_android android_semantics_integration_test - bringup: true # Flaky $expectedSemanticsIntegrationTestNewIssueURL - builder: Mac_android android_semantics_integration_test - presubmit: false - scheduler: luci - properties: - tags: > - ["devicelab"] - task_name: android_semantics_integration_test - - name: Mac_android ignore_myflakiness - bringup: true - builder: Mac_android ignore_myflakiness - presubmit: false - scheduler: luci - properties: - ignore_flakiness: "true" - tags: > - ["devicelab"] - task_name: ignore_myflakiness - - name: Linux analyze - builder: Linux analyze - scheduler: luci - properties: - tags: > - ["framework","hostonly"] - - name: Windows framework_tests_misc - builder: Windows framework_tests_misc - presubmit: false - scheduler: luci - properties: - tags: > - ["shard"] -'''; -const String expectedSemanticsIntegrationTestPullRequestTitle = - 'Marks Mac_android android_semantics_integration_test to be flaky'; -const String expectedSemanticsIntegrationTestPullRequestBody = ''' - -Issue link: $expectedSemanticsIntegrationTestNewIssueURL -'''; - -final List limitedNumberOfBuildsResponse = [ - BuilderStatistic( - name: 'Mac_android android_semantics_integration_test', - flakyRate: 0.25, - flakyBuilds: ['103', '102', '101'], - succeededBuilds: ['201'], - recentCommit: 'abc', - flakyBuildOfRecentCommit: '103', - flakyNumber: 1, - totalNumber: 4, - ), -]; - -const String expectedLimitedNumberOfBuildsResponseBody = ''' - - -The post-submit test builder `Mac_android android_semantics_integration_test` had a flaky ratio 25.00% for the past (up to) 100 commits, which is above our 2.00% threshold. - -One recent flaky example for a same commit: https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/103 -Commit: https://github.com/flutter/flutter/commit/abc - -Flaky builds: -https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/103 -https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/102 -https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/101 - -Recent test runs: -https://flutter-dashboard.appspot.com/#/build?taskFilter=Mac_android%20android_semantics_integration_test - -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). -'''; - -final List analyzeTestResponse = [ - BuilderStatistic( - name: 'Linux analyze', - flakyRate: 0.03, - flakyBuilds: ['103', '102', '101'], - succeededBuilds: ['203', '202', '201'], - recentCommit: 'abc', - flakyBuildOfRecentCommit: '103', - flakyNumber: 3, - totalNumber: 6, - ), -]; -const String expectedAnalyzeTestResponseAssignee = 'HansMuller'; -const List expectedAnalyzeTestResponseLabels = [ - 'c: flake', - 'P0', - 'team-framework', -]; - -final List frameworkTestResponse = [ - BuilderStatistic( - name: 'Windows framework_tests_misc', - flakyRate: 0.03, - flakyBuilds: ['103', '102', '101'], - succeededBuilds: ['203', '202', '201'], - recentCommit: 'abc', - flakyBuildOfRecentCommit: '103', - flakyNumber: 3, - totalNumber: 6, - ), -]; - -final List unknownTestResponse = [ - BuilderStatistic( - name: 'Windows some_unknown_test', - flakyRate: 0.03, - flakyBuilds: ['103', '102', '101'], - succeededBuilds: ['203', '202', '201'], - recentCommit: 'abc', - flakyBuildOfRecentCommit: '103', - flakyNumber: 3, - totalNumber: 6, - ), -]; -const String expectedFrameworkTestResponseAssignee = 'HansMuller'; -const List expectedFrameworkTestResponseLabels = [ - 'c: flake', - 'P0', - 'team-framework', -]; - -String gitHubEncode(String source) { - final List utf8Characters = utf8.encode(source); - final String base64encoded = base64Encode(utf8Characters); - return base64encoded; -} diff --git a/app_dart/test/request_handlers/flaky_handler_utiles_test.dart b/app_dart/test/request_handlers/flaky_handler_utiles_test.dart deleted file mode 100644 index 81645c4da..000000000 --- a/app_dart/test/request_handlers/flaky_handler_utiles_test.dart +++ /dev/null @@ -1,158 +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:cocoon_service/protos.dart' as pb; -import 'package:cocoon_service/src/request_handlers/flaky_handler_utils.dart'; -import 'package:cocoon_service/src/service/config.dart'; -import 'package:cocoon_service/src/service/github_service.dart'; -import 'package:github/github.dart' hide Team; -import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; - -import '../src/datastore/fake_config.dart'; -import '../src/utilities/mocks.dart'; - -void main() { - group('Gets test ownership', () { - String testOwnersContent; - - group('framework host only', () { - test('returns correct owner when no mulitple tests share the same file', () async { - final pb.Target target = pb.Target(name: 'Linux abc'); - testOwnersContent = ''' -## Host only framework tests -# Linux abc -abc_test.sh @ghi @flutter/engine -## Firebase tests -'''; - final TestOwnership ownership = getTestOwnership(target, BuilderType.frameworkHostOnly, testOwnersContent); - expect(ownership.owner, 'ghi'); - expect(ownership.team, Team.engine); - }); - - test('returns correct owner when mulitple tests share the same file', () async { - final pb.Target target1 = pb.Target(name: 'Linux abc'); - final pb.Target target2 = pb.Target(name: 'Linux def'); - testOwnersContent = ''' -## Host only framework tests -# Linux abc -# Linu def -abc_test.sh @ghi @flutter/framework -## Firebase tests -'''; - final TestOwnership ownership1 = getTestOwnership(target1, BuilderType.frameworkHostOnly, testOwnersContent); - expect(ownership1.owner, 'ghi'); - expect(ownership1.team, Team.framework); - final TestOwnership ownership2 = getTestOwnership(target2, BuilderType.frameworkHostOnly, testOwnersContent); - expect(ownership2.owner, 'ghi'); - expect(ownership2.team, Team.framework); - }); - }); - - group('firebaselab only', () { - test('returns correct owner', () async { - final pb.Target target = pb.Target(name: 'Linux firebase_abc'); - testOwnersContent = ''' -## Firebase tests -# Linux abc -/test/abc @def @flutter/tool -## Shards tests -'''; - final TestOwnership ownership = getTestOwnership(target, BuilderType.firebaselab, testOwnersContent); - expect(ownership.owner, 'def'); - expect(ownership.team, Team.tool); - }); - }); - - group('devicelab tests', () { - test('returns correct owner', () async { - final pb.Target target = pb.Target(name: 'abc', properties: {'task_name': 'abc'}); - testOwnersContent = ''' -## Linux Android DeviceLab tests -/dev/devicelab/bin/tasks/abc.dart @def @flutter/web - -## Host only framework tests -'''; - final TestOwnership ownership = getTestOwnership(target, BuilderType.devicelab, testOwnersContent); - expect(ownership.owner, 'def'); - expect(ownership.team, Team.web); - }); - }); - - group('shards tests', () { - test('returns correct owner', () async { - final pb.Target target = pb.Target(name: 'Linux abc'); - testOwnersContent = ''' -## Shards tests -# -# abc @def @flutter/engine -'''; - final TestOwnership ownership = getTestOwnership(target, BuilderType.shard, testOwnersContent); - expect(ownership.owner, 'def'); - expect(ownership.team, Team.engine); - }); - }); - - group('getExistingPRs', () { - test('throws more detailed logs for bad prs with inappropriate body meta tag', () async { - final MockGitHub mockGitHubClient = MockGitHub(); - final MockPullRequestsService mockPullRequestsService = MockPullRequestsService(); - const String expectedHtml = 'https://someurl'; - // when gets existing marks flaky prs. - when(mockPullRequestsService.list(captureAny)).thenAnswer((Invocation invocation) { - return Stream.value( - PullRequest( - htmlUrl: expectedHtml, - body: ''' -''', - ), - ); - }); - when(mockGitHubClient.pullRequests).thenReturn(mockPullRequestsService); - final FakeConfig config = FakeConfig( - githubService: GithubService(mockGitHubClient), - ); - expect( - () => getExistingPRs(config.githubService!, Config.flutterSlug), - throwsA( - predicate((String e) { - return e.contains('Unable to parse body of $expectedHtml'); - }), - ), - ); - }); - - test('handles PRs with empty body message', () async { - final MockGitHub mockGitHubClient = MockGitHub(); - final MockPullRequestsService mockPullRequestsService = MockPullRequestsService(); - const String expectedHtml = 'https://someurl'; - when(mockPullRequestsService.list(captureAny)).thenAnswer((Invocation invocation) { - return Stream.value( - PullRequest( - htmlUrl: expectedHtml, - ), - ); - }); - when(mockGitHubClient.pullRequests).thenReturn(mockPullRequestsService); - final FakeConfig config = FakeConfig( - githubService: GithubService(mockGitHubClient), - ); - expect(await getExistingPRs(config.githubService!, Config.flutterSlug), {}); - }); - }); - }); - group('Gets team label', () { - test('returns correct label when matched', () async { - expect(getTeamLabelFromTeam(Team.infra), kInfraLabel); - }); - - test('returns null when not matched', () async { - expect(getTeamLabelFromTeam(Team.unknown), null); - }); - }); -} diff --git a/app_dart/test/request_handlers/flush_cache_test.dart b/app_dart/test/request_handlers/flush_cache_test.dart deleted file mode 100644 index d7c0d99bb..000000000 --- a/app_dart/test/request_handlers/flush_cache_test.dart +++ /dev/null @@ -1,67 +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:typed_data'; - -import 'package:cocoon_service/src/request_handlers/flush_cache.dart'; -import 'package:cocoon_service/src/request_handling/exceptions.dart'; -import 'package:cocoon_service/src/service/cache_service.dart'; -import 'package:cocoon_service/src/service/config.dart'; -import 'package:test/test.dart'; - -import '../src/datastore/fake_config.dart'; -import '../src/request_handling/api_request_handler_tester.dart'; -import '../src/request_handling/fake_authentication.dart'; -import '../src/request_handling/fake_http.dart'; - -void main() { - group('FlushCache', () { - FakeConfig config; - late ApiRequestHandlerTester tester; - late FlushCache handler; - late CacheService cache; - - setUp(() { - tester = ApiRequestHandlerTester(); - cache = CacheService(inMemory: true); - config = FakeConfig(); - handler = FlushCache( - config: config, - authenticationProvider: FakeAuthenticationProvider(), - cache: cache, - ); - }); - - test('cache is empty when given an existing config key', () async { - const String cacheKey = 'test'; - await cache.set( - Config.configCacheName, - cacheKey, - Uint8List.fromList('123'.codeUnits), - ); - - tester.request = FakeHttpRequest( - queryParametersValue: { - FlushCache.cacheKeyParam: cacheKey, - }, - ); - await tester.get(handler); - - expect(await cache.getOrCreate(Config.configCacheName, cacheKey, createFn: null), null); - }); - - test('raises error if cache key not passed', () async { - expect(tester.get(handler), throwsA(isA())); - }); - - test('raises error if cache key does not exist', () async { - tester.request = FakeHttpRequest( - queryParametersValue: { - FlushCache.cacheKeyParam: 'abc', - }, - ); - expect(tester.get(handler), throwsA(isA())); - }); - }); -} diff --git a/app_dart/test/request_handlers/get_green_commits_test.dart b/app_dart/test/request_handlers/get_green_commits_test.dart deleted file mode 100644 index 6e9df501d..000000000 --- a/app_dart/test/request_handlers/get_green_commits_test.dart +++ /dev/null @@ -1,199 +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: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:cocoon_service/src/service/build_status_provider.dart'; -import 'package:cocoon_service/src/service/datastore.dart'; -import 'package:gcloud/db.dart'; -import 'package:test/test.dart'; - -import '../src/datastore/fake_config.dart'; -import '../src/request_handling/fake_authentication.dart'; -import '../src/request_handling/fake_http.dart'; -import '../src/request_handling/request_handler_tester.dart'; -import '../src/service/fake_build_status_provider.dart'; -import '../src/utilities/entity_generators.dart'; - -void main() { - group('GetGreenCommits', () { - late FakeConfig config; - FakeClientContext clientContext; - FakeKeyHelper keyHelper; - FakeBuildStatusService buildStatusService; - late RequestHandlerTester tester; - late GetGreenCommits handler; - - final Commit commit1 = generateCommit(1, timestamp: 3, sha: 'ea28a9c34dc701de891eaf74503ca4717019f829'); - final Commit commit2 = generateCommit(2, timestamp: 1, sha: 'd5b0b3c8d1c5fd89302089077ccabbcfaae045e4'); - final Commit commitBranched = generateCommit( - 2, - timestamp: 1, - sha: 'ffffffffffffffffffffffffffffffffaae045e4', - branch: 'flutter-2.13-candidate.0', - ); - - final Task task1Succeed = generateTask(1, status: Task.statusSucceeded); - final Task task2Failed = generateTask(2, status: Task.statusFailed); // should fail if included - final Task task3FailedFlaky = - generateTask(3, status: Task.statusFailed, isFlaky: true); // should succeed if included because `bringup: true` - final Task task4SucceedFlaky = generateTask(4, status: Task.statusSucceeded, isFlaky: true); - - final Stage stageOneSucceed = - Stage('cocoon', commit1, [task1Succeed], Task.statusInProgress); // should scceed, since task 1 succeed - final Stage stageFailed = Stage( - 'luci', - commit1, - [task1Succeed, task2Failed], - Task.statusInProgress, - ); // should fail, since task 1 succeed and task2 fail - final Stage stageMultipleSucceed = Stage( - 'cocoon', - commit2, - [task1Succeed, task4SucceedFlaky], - Task.statusInProgress, - ); // should succeed, since both task 1 and task 4 succeed - final Stage stageFailedFlaky = Stage( - 'luci', - commit2, - [task1Succeed, task3FailedFlaky], - Task.statusInProgress, - ); // should succeed, even though it includes task 3 - - Future?> decodeHandlerBody() async { - final Body body = await tester.get(handler); - return (await utf8.decoder.bind(body.serialize() as Stream>).transform(json.decoder).single - as List) - .cast(); - } - - setUp(() { - clientContext = FakeClientContext(); - keyHelper = FakeKeyHelper(applicationContext: clientContext.applicationContext); - tester = RequestHandlerTester(); - config = FakeConfig(keyHelperValue: keyHelper); - buildStatusService = FakeBuildStatusService(commitStatuses: []); - handler = GetGreenCommits( - config: config, - datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5), - buildStatusProvider: (_) => buildStatusService, - ); - }); - - test('no green commits', () async { - final List result = (await decodeHandlerBody())!; - expect(result, isEmpty); - }); - - test('should return commits with all tasks succeed', () async { - buildStatusService = FakeBuildStatusService( - commitStatuses: [ - CommitStatus(commit1, [stageOneSucceed]), - CommitStatus(commit2, [stageOneSucceed, stageMultipleSucceed]), - ], - ); - handler = GetGreenCommits( - config: config, - datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5), - buildStatusProvider: (_) => buildStatusService, - ); - - final List result = (await decodeHandlerBody())!; - - expect(result.length, 2); - expect(result, [ - commit2.sha!, - commit1.sha!, - ]); - }); - - test('should fail commits that have failed task without [bringup: true] label', () async { - buildStatusService = FakeBuildStatusService( - commitStatuses: [ - CommitStatus(commit1, [stageFailed]), - CommitStatus(commit2, [stageOneSucceed, stageMultipleSucceed]), - ], - ); - handler = GetGreenCommits( - config: config, - datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5), - buildStatusProvider: (_) => buildStatusService, - ); - - final List result = (await decodeHandlerBody())!; - - expect(result.length, 1); - expect(result, [commit2.sha!]); - }); - - test('should return commits with failed tasks but with `bringup: true` label', () async { - buildStatusService = FakeBuildStatusService( - commitStatuses: [ - CommitStatus(commit1, [stageFailed]), - CommitStatus(commit2, [stageFailedFlaky]), - ], - ); - handler = GetGreenCommits( - config: config, - datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5), - buildStatusProvider: (_) => buildStatusService, - ); - - final List result = (await decodeHandlerBody())!; - - expect(result.length, 1); - expect(result, [commit2.sha!]); - }); - - test('should return commits with both flaky and succeeded tasks', () async { - buildStatusService = FakeBuildStatusService( - commitStatuses: [ - CommitStatus(commit1, [stageOneSucceed, stageMultipleSucceed]), - CommitStatus(commit2, [stageOneSucceed, stageFailedFlaky]), - ], - ); - handler = GetGreenCommits( - config: config, - datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5), - buildStatusProvider: (_) => buildStatusService, - ); - - final List result = (await decodeHandlerBody())!; - - expect(result.length, 2); - expect(result, [ - commit2.sha!, - commit1.sha!, - ]); - }); - - test('should return branched commits', () async { - buildStatusService = FakeBuildStatusService( - commitStatuses: [ - CommitStatus(commitBranched, [stageOneSucceed]), - ], - ); - tester.request = FakeHttpRequest( - queryParametersValue: { - GetGreenCommits.kBranchParam: commitBranched.branch!, - }, - ); - handler = GetGreenCommits( - config: config, - datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5), - buildStatusProvider: (_) => buildStatusService, - ); - - final List result = (await decodeHandlerBody())!; - - expect(result, [ - commitBranched.sha!, - ]); - }); - }); -} diff --git a/app_dart/test/request_handlers/get_status_test.dart b/app_dart/test/request_handlers/get_status_test.dart deleted file mode 100644 index 23bcd23c7..000000000 --- a/app_dart/test/request_handlers/get_status_test.dart +++ /dev/null @@ -1,191 +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/appengine/commit.dart'; -import 'package:cocoon_service/src/model/appengine/stage.dart'; -import 'package:cocoon_service/src/request_handlers/get_status.dart'; -import 'package:cocoon_service/src/request_handling/body.dart'; -import 'package:cocoon_service/src/service/build_status_provider.dart'; -import 'package:cocoon_service/src/service/datastore.dart'; -import 'package:gcloud/db.dart'; -import 'package:test/test.dart'; - -import '../src/datastore/fake_config.dart'; -import '../src/request_handling/fake_authentication.dart'; -import '../src/request_handling/fake_http.dart'; -import '../src/request_handling/request_handler_tester.dart'; -import '../src/service/fake_build_status_provider.dart'; - -void main() { - group('GetStatus', () { - late FakeConfig config; - FakeClientContext clientContext; - FakeKeyHelper keyHelper; - FakeBuildStatusService buildStatusService; - late RequestHandlerTester tester; - late GetStatus handler; - - late Commit commit1; - late Commit commit2; - - Future decodeHandlerBody() async { - final Body body = await tester.get(handler); - return await utf8.decoder.bind(body.serialize() as Stream>).transform(json.decoder).single as T?; - } - - setUp(() { - clientContext = FakeClientContext(); - keyHelper = FakeKeyHelper(applicationContext: clientContext.applicationContext); - tester = RequestHandlerTester(); - config = FakeConfig(keyHelperValue: keyHelper); - buildStatusService = FakeBuildStatusService(commitStatuses: []); - handler = GetStatus( - config: config, - datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5), - buildStatusProvider: (_) => buildStatusService, - ); - commit1 = Commit( - key: config.db.emptyKey.append(Commit, id: 'flutter/flutter/ea28a9c34dc701de891eaf74503ca4717019f829'), - repository: 'flutter/flutter', - sha: 'ea28a9c34dc701de891eaf74503ca4717019f829', - timestamp: 3, - message: 'test message 1', - branch: 'master', - ); - commit2 = Commit( - key: config.db.emptyKey.append(Commit, id: 'flutter/flutter/d5b0b3c8d1c5fd89302089077ccabbcfaae045e4'), - repository: 'flutter/flutter', - sha: 'd5b0b3c8d1c5fd89302089077ccabbcfaae045e4', - timestamp: 1, - message: 'test message 2', - branch: 'master', - ); - }); - - test('no statuses', () async { - final Map result = (await decodeHandlerBody())!; - expect(result['Statuses'], isEmpty); - }); - - test('reports statuses without input commit key', () async { - config.db.values[commit1.key] = commit1; - config.db.values[commit2.key] = commit2; - buildStatusService = FakeBuildStatusService( - commitStatuses: [CommitStatus(commit1, const []), CommitStatus(commit2, const [])], - ); - handler = GetStatus( - config: config, - datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5), - buildStatusProvider: (_) => buildStatusService, - ); - - final Map result = (await decodeHandlerBody())!; - - expect(result['Statuses'].length, 2); - }); - - test('reports statuses with input commit key', () async { - final Commit commit1 = Commit( - key: config.db.emptyKey.append(Commit, id: 'flutter/flutter/ea28a9c34dc701de891eaf74503ca4717019f829'), - repository: 'flutter/flutter', - sha: 'ea28a9c34dc701de891eaf74503ca4717019f829', - timestamp: 3, - message: 'test message 1', - branch: 'master', - ); - final Commit commit2 = Commit( - key: config.db.emptyKey.append(Commit, id: 'flutter/flutter/d5b0b3c8d1c5fd89302089077ccabbcfaae045e4'), - repository: 'flutter/flutter', - sha: 'd5b0b3c8d1c5fd89302089077ccabbcfaae045e4', - timestamp: 1, - message: 'test message 2', - branch: 'master', - ); - config.db.values[commit1.key] = commit1; - config.db.values[commit2.key] = commit2; - buildStatusService = FakeBuildStatusService( - commitStatuses: [CommitStatus(commit1, const []), CommitStatus(commit2, const [])], - ); - handler = GetStatus( - config: config, - datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5), - buildStatusProvider: (_) => buildStatusService, - ); - - const String expectedLastCommitKeyEncoded = - 'ahNzfmZsdXR0ZXItZGFzaGJvYXJkckcLEglDaGVja2xpc3QiOGZsdXR0ZXIvZmx1dHRlci9lYTI4YTljMzRkYzcwMWRlODkxZWFmNzQ1MDNjYTQ3MTcwMTlmODI5DA'; - - tester.request = FakeHttpRequest( - queryParametersValue: { - GetStatus.kLastCommitKeyParam: expectedLastCommitKeyEncoded, - }, - ); - final Map result = (await decodeHandlerBody())!; - - expect(result['Statuses'].first, { - 'Checklist': { - 'Key': - 'ahFmbHV0dGVyLWRhc2hib2FyZHJHCxIJQ2hlY2tsaXN0IjhmbHV0dGVyL2ZsdXR0ZXIvZDViMGIzYzhkMWM1ZmQ4OTMwMjA4OTA3N2NjYWJiY2ZhYWUwNDVlNAyiAQlbZGVmYXVsdF0', - 'Checklist': { - 'FlutterRepositoryPath': 'flutter/flutter', - 'CreateTimestamp': 1, - 'Commit': { - 'Sha': 'd5b0b3c8d1c5fd89302089077ccabbcfaae045e4', - 'Message': 'test message 2', - 'Author': {'Login': null, 'avatar_url': null}, - }, - 'Branch': 'master', - }, - }, - 'Stages': [], - }); - }); - - test('reports statuses with input branch', () async { - commit2.branch = 'flutter-1.1-candidate.1'; - config.db.values[commit1.key] = commit1; - config.db.values[commit2.key] = commit2; - buildStatusService = FakeBuildStatusService( - commitStatuses: [CommitStatus(commit1, const []), CommitStatus(commit2, const [])], - ); - handler = GetStatus( - config: config, - datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5), - buildStatusProvider: (_) => buildStatusService, - ); - - const String branch = 'flutter-1.1-candidate.1'; - - expect(config.db.values.length, 2); - - tester.request = FakeHttpRequest( - queryParametersValue: { - GetStatus.kBranchParam: branch, - }, - ); - final Map result = (await decodeHandlerBody())!; - - expect(result['Statuses'].length, 1); - expect(result['Statuses'].first, { - 'Checklist': { - 'Key': - 'ahFmbHV0dGVyLWRhc2hib2FyZHJHCxIJQ2hlY2tsaXN0IjhmbHV0dGVyL2ZsdXR0ZXIvZDViMGIzYzhkMWM1ZmQ4OTMwMjA4OTA3N2NjYWJiY2ZhYWUwNDVlNAyiAQlbZGVmYXVsdF0', - 'Checklist': { - 'FlutterRepositoryPath': 'flutter/flutter', - 'CreateTimestamp': 1, - 'Commit': { - 'Sha': 'd5b0b3c8d1c5fd89302089077ccabbcfaae045e4', - 'Message': 'test message 2', - 'Author': {'Login': null, 'avatar_url': null}, - }, - 'Branch': 'flutter-1.1-candidate.1', - }, - }, - 'Stages': [], - }); - }); - }); -} diff --git a/app_dart/test/request_handlers/github/webhook_subscription_test.dart b/app_dart/test/request_handlers/github/webhook_subscription_test.dart deleted file mode 100644 index d54f76fd1..000000000 --- a/app_dart/test/request_handlers/github/webhook_subscription_test.dart +++ /dev/null @@ -1,2322 +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/model/appengine/commit.dart'; -import 'package:cocoon_service/src/model/luci/buildbucket.dart'; -import 'package:cocoon_service/src/request_handlers/github/webhook_subscription.dart'; -import 'package:cocoon_service/src/service/cache_service.dart'; -import 'package:cocoon_service/src/service/config.dart'; -import 'package:cocoon_service/src/service/datastore.dart'; - -import 'package:github/github.dart' hide Branch; -import 'package:googleapis/bigquery/v2.dart'; -import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; - -import '../../src/datastore/fake_config.dart'; -import '../../src/datastore/fake_datastore.dart'; -import '../../src/request_handling/fake_http.dart'; -import '../../src/request_handling/subscription_tester.dart'; -import '../../src/service/fake_buildbucket.dart'; -import '../../src/service/fake_github_service.dart'; -import '../../src/service/fake_gerrit_service.dart'; -import '../../src/service/fake_scheduler.dart'; -import '../../src/utilities/entity_generators.dart'; -import '../../src/utilities/mocks.dart'; -import '../../src/utilities/webhook_generators.dart'; - -void main() { - late GithubWebhookSubscription webhook; - late FakeBuildBucketClient fakeBuildBucketClient; - late FakeConfig config; - late FakeDatastoreDB db; - late FakeGithubService githubService; - late FakeHttpRequest request; - late FakeScheduler scheduler; - late FakeGerritService gerritService; - late MockCommitService commitService; - late MockGitHub gitHubClient; - late MockGithubChecksUtil mockGithubChecksUtil; - late MockGithubChecksService mockGithubChecksService; - late MockIssuesService issuesService; - late MockPullRequestsService pullRequestsService; - late SubscriptionTester tester; - - /// Name of an example release base branch name. - const String kReleaseBaseRef = 'flutter-2.12-candidate.4'; - - /// Name of an example release head branch name. - const String kReleaseHeadRef = 'cherrypicks-flutter-2.12-candidate.4'; - - setUp(() { - request = FakeHttpRequest(); - db = FakeDatastoreDB(); - gitHubClient = MockGitHub(); - githubService = FakeGithubService(); - commitService = MockCommitService(); - final MockTabledataResource tabledataResource = MockTabledataResource(); - when(tabledataResource.insertAll(any, any, any, any)).thenAnswer((_) async => TableDataInsertAllResponse()); - config = FakeConfig( - dbValue: db, - githubClient: gitHubClient, - githubService: githubService, - githubOAuthTokenValue: 'githubOAuthKey', - missingTestsPullRequestMessageValue: 'missingTestPullRequestMessage', - releaseBranchPullRequestMessageValue: 'releaseBranchPullRequestMessage', - rollerAccountsValue: const { - 'skia-flutter-autoroll', - 'engine-flutter-autoroll', - 'dependabot', - 'dependabot[bot]', - }, - tabledataResource: tabledataResource, - wrongHeadBranchPullRequestMessageValue: 'wrongHeadBranchPullRequestMessage', - wrongBaseBranchPullRequestMessageValue: '{{target_branch}} -> {{default_branch}}', - ); - issuesService = MockIssuesService(); - when(issuesService.addLabelsToIssue(any, any, any)).thenAnswer((_) async => []); - when(issuesService.createComment(any, any, any)).thenAnswer((_) async => IssueComment()); - when(issuesService.listCommentsByIssue(any, any)) - .thenAnswer((_) => Stream.fromIterable([IssueComment()])); - pullRequestsService = MockPullRequestsService(); - when(pullRequestsService.listFiles(Config.flutterSlug, any)) - .thenAnswer((_) => const Stream.empty()); - when(pullRequestsService.edit(any, any, title: anyNamed('title'), state: anyNamed('state'), base: anyNamed('base'))) - .thenAnswer((_) async => PullRequest()); - fakeBuildBucketClient = FakeBuildBucketClient(); - mockGithubChecksUtil = MockGithubChecksUtil(); - scheduler = FakeScheduler( - config: config, - buildbucket: fakeBuildBucketClient, - githubChecksUtil: mockGithubChecksUtil, - ); - tester = SubscriptionTester(request: request); - - mockGithubChecksService = MockGithubChecksService(); - when(gitHubClient.issues).thenReturn(issuesService); - when(gitHubClient.pullRequests).thenReturn(pullRequestsService); - when(mockGithubChecksUtil.createCheckRun(any, any, any, any, output: anyNamed('output'))).thenAnswer((_) async { - return CheckRun.fromJson(const { - 'id': 1, - 'started_at': '2020-05-10T02:49:31Z', - 'check_suite': {'id': 2}, - }); - }); - - gerritService = FakeGerritService(); - webhook = GithubWebhookSubscription( - config: config, - cache: CacheService(inMemory: true), - datastoreProvider: (_) => DatastoreService(config.db, 5), - gerritService: gerritService, - githubChecksService: mockGithubChecksService, - scheduler: scheduler, - commitService: commitService, - ); - }); - - group('github webhook pull_request event', () { - test('Closes PR opened from dev', () async { - const int issueNumber = 123; - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - headRef: 'dev', - ); - when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer( - (_) => Stream.value( - PullRequestFile()..filename = 'packages/flutter/blah.dart', - ), - ); - - when(issuesService.listCommentsByIssue(Config.flutterSlug, issueNumber)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = 'some other comment', - ), - ); - - await tester.post(webhook); - - verify( - pullRequestsService.edit( - Config.flutterSlug, - issueNumber, - state: 'closed', - ), - ).called(1); - - verify( - issuesService.createComment( - Config.flutterSlug, - issueNumber, - argThat(contains(config.wrongHeadBranchPullRequestMessageValue)), - ), - ).called(1); - }); - - test('No action against candidate branches', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - baseRef: 'flutter-2.13-candidate.0', - ); - - when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer( - (_) => Stream.value( - PullRequestFile()..filename = 'packages/flutter/blah.dart', - ), - ); - - when(issuesService.listCommentsByIssue(Config.flutterSlug, issueNumber)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = 'some other comment', - ), - ); - - await tester.post(webhook); - - verifyNever( - pullRequestsService.edit( - Config.flutterSlug, - issueNumber, - base: kDefaultBranchName, - ), - ); - - verifyNever( - issuesService.createComment( - Config.flutterSlug, - issueNumber, - argThat(contains('-> master')), - ), - ); - }); - - test('Acts on opened against dev', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - baseRef: 'dev', - ); - - when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer( - (_) => Stream.value( - PullRequestFile()..filename = 'packages/flutter/blah.dart', - ), - ); - - when(issuesService.listCommentsByIssue(Config.flutterSlug, issueNumber)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = 'some other comment', - ), - ); - - await tester.post(webhook); - - verify( - pullRequestsService.edit( - Config.flutterSlug, - issueNumber, - base: kDefaultBranchName, - ), - ).called(1); - - verify( - issuesService.createComment( - Config.flutterSlug, - issueNumber, - argThat(contains('dev -> master')), - ), - ).called(1); - }); - - test('Acts on closed, cancels presubmit targets', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'closed', - number: issueNumber, - baseRef: 'dev', - merged: false, - ); - - await tester.post(webhook); - - expect(scheduler.cancelPreSubmitTargetsCallCnt, 1); - expect(scheduler.addPullRequestCallCnt, 0); - }); - - test('Acts on closed, cancels presubmit targets, add pr for postsubmit target create', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'closed', - number: issueNumber, - baseRef: 'dev', - merged: true, - baseSha: 'sha1', - mergeCommitSha: 'sha2', - ); - - await tester.post(webhook); - - expect(scheduler.cancelPreSubmitTargetsCallCnt, 1); - expect(scheduler.addPullRequestCallCnt, 1); - }); - - test('Acts on opened against master when default is main', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - baseRef: 'master', - slug: Config.engineSlug, - ); - - when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer( - (_) => Stream.value( - PullRequestFile()..filename = 'packages/flutter/blah.dart', - ), - ); - - when(issuesService.listCommentsByIssue(Config.engineSlug, issueNumber)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = 'some other comment', - ), - ); - - await tester.post(webhook); - - verify( - pullRequestsService.edit( - Config.engineSlug, - issueNumber, - base: 'main', - ), - ).called(1); - - verify( - issuesService.createComment( - Config.engineSlug, - issueNumber, - argThat(contains('master -> main')), - ), - ).called(1); - }); - - // We already schedule checks when a draft is opened, don't need to re-test - // just because it was marked ready for review - test('Does nothing on ready_for_review', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'ready_for_review', - number: issueNumber, - ); - bool batchRequestCalled = false; - - Future getBatchResponse() async { - batchRequestCalled = true; - fail('Marking a draft ready for review should not trigger new builds'); - } - - fakeBuildBucketClient.batchResponse = getBatchResponse; - - await tester.post(webhook); - - expect(batchRequestCalled, isFalse); - }); - - test('Triggers builds when opening a draft PR', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - isDraft: true, - ); - bool batchRequestCalled = false; - - Future getBatchResponse() async { - batchRequestCalled = true; - return BatchResponse( - responses: [ - Response( - searchBuilds: SearchBuildsResponse( - builds: [ - generateBuild(999, name: 'Linux', status: Status.ended), - ], - ), - ), - Response( - searchBuilds: SearchBuildsResponse( - builds: [ - generateBuild(998, name: 'Linux', status: Status.ended), - ], - ), - ), - ], - ); - } - - fakeBuildBucketClient.batchResponse = getBatchResponse; - - await tester.post(webhook); - - expect(batchRequestCalled, isTrue); - expect(scheduler.cancelPreSubmitTargetsCallCnt, 1); - }); - - test('Does nothing against cherry pick PR', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - baseRef: 'flutter-1.20-candidate.7', - headRef: 'cherrypicks-flutter-1.20-candidate.7', - ); - when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer( - (_) => Stream.value( - PullRequestFile()..filename = 'packages/flutter/blah.dart', - ), - ); - - when(issuesService.listCommentsByIssue(Config.flutterSlug, issueNumber)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = 'some other comment', - ), - ); - - await tester.post(webhook); - - verifyNever( - pullRequestsService.edit( - Config.flutterSlug, - issueNumber, - base: kDefaultBranchName, - ), - ); - - verifyNever( - issuesService.createComment( - Config.flutterSlug, - issueNumber, - argThat(contains(config.wrongBaseBranchPullRequestMessage)), - ), - ); - }); - - test('release PRs are approved', () async { - const int issueNumber = 123; - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - // Base is where the PR will merge into - baseRef: 'flutter-2.13-candidate.0', - login: 'dart-flutter-releaser', - ); - when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)) - .thenAnswer((_) => const Stream.empty()); - when(pullRequestsService.createReview(Config.flutterSlug, any)) - .thenAnswer((_) async => PullRequestReview(id: 123, user: User())); - - await tester.post(webhook); - - final List reviews = verify(pullRequestsService.createReview(Config.flutterSlug, captureAny)).captured; - expect(reviews.length, 1); - final CreatePullRequestReview review = reviews.single as CreatePullRequestReview; - expect(review.event, 'APPROVE'); - }); - - test('fake release PRs are not approved', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - // Base is where the PR will merge into - baseRef: 'master', - // Head is the branch from the fork - headRef: 'flutter-2.13-candidate.0', - login: 'dart-flutter-releaser', - ); - when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)) - .thenAnswer((_) => const Stream.empty()); - when(pullRequestsService.createReview(Config.flutterSlug, any)) - .thenAnswer((_) async => PullRequestReview(id: 123, user: User())); - - await tester.post(webhook); - - verifyNever(pullRequestsService.createReview(Config.flutterSlug, captureAny)); - }); - - test('release PRs are not approved for outsider PRs', () async { - const int issueNumber = 123; - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - headRef: 'flutter-2.13-candidate.0', - ); - when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)) - .thenAnswer((_) => const Stream.empty()); - when(pullRequestsService.createReview(Config.flutterSlug, any)) - .thenAnswer((_) async => PullRequestReview(id: 123, user: User())); - - await tester.post(webhook); - - verifyNever(pullRequestsService.createReview(Config.flutterSlug, any)); - }); - - test('Framework labels PRs, comment if no tests', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - ); - when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer( - (_) => Stream.value( - PullRequestFile()..filename = 'packages/flutter/blah.dart', - ), - ); - - when(issuesService.listCommentsByIssue(Config.flutterSlug, issueNumber)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = 'some other comment', - ), - ); - - await tester.post(webhook); - - verify( - issuesService.createComment( - Config.flutterSlug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ).called(1); - }); - - group('Auto-roller accounts do not label Framework PR with test label or comment.', () { - final Set inputs = { - 'skia-flutter-autoroll', - 'dependabot', - }; - - for (String element in inputs) { - test('Framework does not label PR with no tests label if author is $element', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - login: element, - ); - - final RepositorySlug slug = RepositorySlug('flutter', 'flutter'); - - when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer( - (_) => Stream.value( - PullRequestFile()..filename = 'packages/flutter/blah.dart', - ), - ); - - when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = 'some other comment', - ), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.addLabelsToIssue( - slug, - issueNumber, - any, - ), - ); - - verifyNever( - issuesService.createComment( - slug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - } - }); - - test('Framework does not label PR with no tests label if author is engine-flutter-autoroll', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - login: 'engine-flutter-autoroll', - ); - final RepositorySlug slug = RepositorySlug('flutter', 'flutter'); - - when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer( - (_) => Stream.value( - PullRequestFile()..filename = 'packages/flutter/blah.dart', - ), - ); - - when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = 'some other comment', - ), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.addLabelsToIssue( - slug, - issueNumber, - any, - ), - ); - - verifyNever( - issuesService.createComment( - slug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Framework does not label PR with no tests label if file is test exempt', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - ); - final RepositorySlug slug = RepositorySlug('flutter', 'flutter'); - - when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile()..filename = 'dev/devicelab/lib/versions/gallery.dart', - PullRequestFile()..filename = 'dev/integration_tests/some_package/android/build.gradle', - PullRequestFile()..filename = 'impeller/fixtures/dart_tests.dart', - PullRequestFile()..filename = 'impeller/golden_tests/golden_tests.cc', - PullRequestFile()..filename = 'impeller/playground/playground.cc', - PullRequestFile()..filename = 'shell/platform/embedder/tests/embedder_test_context.cc', - PullRequestFile()..filename = 'shell/platform/embedder/fixtures/main.dart', - ]), - ); - - when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = 'some other comment', - ), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.addLabelsToIssue( - slug, - issueNumber, - any, - ), - ); - - verifyNever( - issuesService.createComment( - slug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Framework labels PRs, comment if no tests including hit_test.dart file', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - ); - final RepositorySlug slug = RepositorySlug('flutter', 'flutter'); - - when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer( - (_) => Stream.value( - PullRequestFile() - ..additionsCount = 10 - ..changesCount = 10 - ..filename = 'packages/flutter/lib/src/gestures/hit_test.dart', - ), - ); - - when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = 'some other comment', - ), - ); - - await tester.post(webhook); - - verify( - issuesService.createComment( - slug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ).called(1); - }); - - test('Framework labels PRs, no dart files', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - ); - final RepositorySlug slug = RepositorySlug('flutter', 'flutter'); - - when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer( - (_) => Stream.value( - PullRequestFile()..filename = 'packages/flutter/blah.md', - ), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.addLabelsToIssue( - slug, - issueNumber, - any, - ), - ); - - verifyNever( - issuesService.createComment( - slug, - issueNumber, - any, - ), - ); - }); - - test('Framework labels PRs, no comment if tests', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - ); - final RepositorySlug slug = RepositorySlug('flutter', 'flutter'); - - when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile()..filename = 'packages/flutter/semantics_test.dart', - PullRequestFile()..filename = 'packages/flutter_tools/blah.dart', - PullRequestFile()..filename = 'packages/flutter_driver/blah.dart', - PullRequestFile()..filename = 'examples/flutter_gallery/blah.dart', - PullRequestFile()..filename = 'dev/bots/test.dart', - PullRequestFile()..filename = 'dev/devicelab/bin/tasks/analyzer_benchmark.dart', - PullRequestFile()..filename = 'bin/internal/engine.version', - PullRequestFile()..filename = 'packages/flutter/lib/src/cupertino/blah.dart', - PullRequestFile()..filename = 'packages/flutter/lib/src/material/blah.dart', - PullRequestFile()..filename = 'packages/flutter_localizations/blah.dart', - ]), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.createComment( - slug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Framework labels dart fix PRs, no comment if tests', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - ); - final RepositorySlug slug = RepositorySlug('flutter', 'flutter'); - - when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile()..filename = 'packages/flutter/test_fixes/material.dart', - PullRequestFile()..filename = 'packages/flutter/test_fixes/material.expect', - ]), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.createComment( - slug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Framework labels bot PR, no comment', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - login: 'fluttergithubbot', - ); - final RepositorySlug slug = RepositorySlug('flutter', 'flutter'); - - when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile()..filename = 'packages/flutter_tools/blah.dart', - PullRequestFile()..filename = 'packages/flutter_driver/blah.dart', - ]), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.createComment( - slug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Framework labels deletion only PR, no test request', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - ); - final RepositorySlug slug = RepositorySlug('flutter', 'flutter'); - - when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile() - ..filename = 'packages/flutter/blah.dart' - ..deletionsCount = 20 - ..additionsCount = 0 - ..changesCount = 20, - ]), - ); - - await tester.post(webhook); - - // The PR here is only deleting code, so no test comment. - verifyNever( - issuesService.createComment( - slug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('PR with additions and deletions is commented and labeled', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - ); - final RepositorySlug slug = RepositorySlug('flutter', 'flutter'); - - when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile() - ..filename = 'packages/flutter/blah.dart' - ..deletionsCount = 20 - ..additionsCount = 1 - ..changesCount = 21, - ]), - ); - - await tester.post(webhook); - - verify( - issuesService.createComment( - slug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ).called(1); - }); - - test('Framework no comment if code has only devicelab test', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - ); - final RepositorySlug slug = RepositorySlug('flutter', 'flutter'); - - when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile()..filename = 'packages/flutter_tools/lib/src/ios/devices.dart', - PullRequestFile()..filename = 'dev/devicelab/lib/tasks/plugin_tests.dart', - ]), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.createComment( - slug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Framework no comment if only dev bots or devicelab changed', () async { - const int issueNumber = 123; - tester.message = generateGithubWebhookMessage(action: 'opened', number: issueNumber); - - when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile()..filename = 'dev/bots/test.dart', - PullRequestFile()..filename = 'dev/devicelab/bin/tasks/analyzer_benchmark.dart', - PullRequestFile()..filename = 'dev/devicelab/lib/tasks/plugin_tests.dart', - PullRequestFile()..filename = 'dev/benchmarks/microbenchmarks/lib/foundation/all_elements_bench.dart', - ]), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.createComment( - Config.flutterSlug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Framework no comment if only .gitignore changed', () async { - const int issueNumber = 123; - tester.message = generateGithubWebhookMessage(action: 'opened', number: issueNumber); - - when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile()..filename = '.gitignore', - PullRequestFile()..filename = 'dev/integration_tests/foo_app/.gitignore', - ]), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.createComment( - any, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Framework no test comment if Objective-C test changed', () async { - const int issueNumber = 123; - tester.message = generateGithubWebhookMessage(action: 'opened', number: issueNumber); - - when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - // Example of real behavior code change. - PullRequestFile() - ..filename = 'packages/flutter_tools/templates/app_shared/macos.tmpl/Runner/Base.lproj/MainMenu.xib', - // Example of Objective-C test. - PullRequestFile()..filename = 'dev/integration_tests/flutter_gallery/macos/RunnerTests/RunnerTests.m', - ]), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.createComment( - Config.flutterSlug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Framework no comment if only AUTHORS changed', () async { - const int issueNumber = 123; - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - ); - - when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile()..filename = 'AUTHORS', - ]), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.createComment( - Config.flutterSlug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Framework no comment if only ci.yamlchanged', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - ); - final RepositorySlug slug = RepositorySlug('flutter', 'flutter'); - - when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile()..filename = '.ci.yaml', - ]), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.createComment( - slug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Framework no comment if only analysis options changed', () async { - const int issueNumber = 123; - tester.message = generateGithubWebhookMessage(action: 'opened', number: issueNumber); - final RepositorySlug slug = RepositorySlug('flutter', 'flutter'); - - when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile()..filename = 'analysis_options.yaml', - ]), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.createComment( - slug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Framework no comment if only CODEOWNERS or TESTOWNERS changed', () async { - const int issueNumber = 123; - tester.message = generateGithubWebhookMessage(action: 'opened', number: issueNumber); - final RepositorySlug slug = RepositorySlug('flutter', 'flutter'); - - when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile()..filename = 'CODEOWNERS', - PullRequestFile()..filename = 'TESTOWNERS', - ]), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.createComment( - slug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - for (String extention in knownCommentCodeExtensions) { - test('Framework no comment if only comments changed .$extention', () async { - const int issueNumber = 123; - tester.message = generateGithubWebhookMessage(action: 'opened', number: issueNumber); - final RepositorySlug slug = RepositorySlug('flutter', 'flutter'); - - const String patch = ''' -@@ -128,7 +128,7 @@ - -/// Insert interesting comment here. -/// --/// More details here, but some of them are wrong. -+/// These are the right details! -void foo() { - int bar = 0; - String baz = ''; -'''; - - when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile() - ..filename = 'packages/foo/lib/foo.$extention' - ..additionsCount = 1 - ..deletionsCount = 1 - ..changesCount = 2 - ..patch = patch, - ]), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.createComment( - slug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - } - - test('Framework labels PRs, no comment if tests (dev/bots/test.dart)', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - ); - - when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile()..filename = 'dev/bots/test.dart', - PullRequestFile()..filename = 'packages/flutter_tools/blah.dart', - PullRequestFile()..filename = 'packages/flutter_driver/blah.dart', - PullRequestFile()..filename = 'examples/flutter_gallery/blah.dart', - PullRequestFile()..filename = 'packages/flutter/lib/src/material/blah.dart', - PullRequestFile()..filename = 'packages/flutter_localizations/blah.dart', - ]), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.createComment( - Config.flutterSlug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Framework labels PRs, no comment if tests (dev/bots/analyze.dart)', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - ); - final RepositorySlug slug = RepositorySlug('flutter', 'flutter'); - - when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile()..filename = 'dev/bots/analyze.dart', - PullRequestFile()..filename = 'packages/flutter_tools/blah.dart', - PullRequestFile()..filename = 'packages/flutter_driver/blah.dart', - PullRequestFile()..filename = 'examples/flutter_gallery/blah.dart', - PullRequestFile()..filename = 'packages/flutter/lib/src/material/blah.dart', - PullRequestFile()..filename = 'packages/flutter_localizations/blah.dart', - ]), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.createComment( - slug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Framework labels PRs, no comment if tests (flutter_tools/test/helper.dart)', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - ); - - when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile()..filename = 'packages/flutter_tools/blah.dart', - PullRequestFile()..filename = 'packages/flutter_tools/test/helper.dart', - ]), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.createComment( - Config.flutterSlug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Framework labels PRs, apply label but no comment when rolling engine version', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - baseRef: kReleaseBaseRef, - headRef: kReleaseHeadRef, - ); - - when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile() - ..filename = 'bin/internal/engine.version' - ..deletionsCount = 20 - ..additionsCount = 1 - ..changesCount = 21, - ]), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.addLabelsToIssue( - Config.flutterSlug, - issueNumber, - any, - ), - ); - - verifyNever( - issuesService.createComment( - Config.flutterSlug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Engine labels PRs, comment if no tests', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - slug: Config.engineSlug, - baseRef: Config.defaultBranch(Config.engineSlug), - ); - final RepositorySlug slug = RepositorySlug('flutter', 'engine'); - - when(pullRequestsService.listFiles(slug, issueNumber)).thenAnswer( - (_) => Stream.value( - PullRequestFile()..filename = 'shell/platform/darwin/ios/framework/Source/boost.mm', - ), - ); - - when(issuesService.listCommentsByIssue(slug, issueNumber)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = 'some other comment', - ), - ); - - await tester.post(webhook); - - verify( - issuesService.createComment( - slug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ).called(1); - }); - - group('Auto-roller accounts do not label Engine PR with test label or comment.', () { - final Set inputs = { - 'engine-flutter-autoroll', - 'dependabot', - 'dependabot[bot]', - }; - - for (String element in inputs) { - test('Engine does not label PR for no tests if author is $element', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - slug: Config.engineSlug, - login: element, - ); - - when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer( - (_) => Stream.value( - PullRequestFile()..filename = 'shell/platform/darwin/ios/framework/Source/boost.mm', - ), - ); - - when(issuesService.listCommentsByIssue(Config.engineSlug, issueNumber)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = 'some other comment', - ), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.createComment( - Config.engineSlug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - } - }); - - test('Engine does not label PR for no tests if on branch', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - slug: Config.engineSlug, - baseRef: 'flutter-3.12-candidate.1', - ); - - when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer( - (_) => Stream.value( - PullRequestFile()..filename = 'shell/platform/darwin/ios/framework/Source/boost.mm', - ), - ); - - when(issuesService.listCommentsByIssue(Config.engineSlug, issueNumber)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = 'some other comment', - ), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.createComment( - Config.engineSlug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Engine does not label PR for no tests if author is skia-flutter-autoroll', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - slug: Config.engineSlug, - login: 'skia-flutter-autoroll', - ); - - when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer( - (_) => Stream.value( - PullRequestFile()..filename = 'shell/platform/darwin/ios/framework/Source/boost.mm', - ), - ); - - when(issuesService.listCommentsByIssue(Config.engineSlug, issueNumber)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = 'some other comment', - ), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.createComment( - Config.engineSlug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Engine labels PRs, no code files', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - baseRef: 'main', - slug: Config.engineSlug, - ); - - when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer( - (_) => Stream.value( - PullRequestFile()..filename = 'DEPS', - ), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.addLabelsToIssue( - Config.engineSlug, - issueNumber, - any, - ), - ); - - verifyNever( - issuesService.createComment( - Config.engineSlug, - issueNumber, - any, - ), - ); - }); - - test('Engine labels PRs, no comment if Java tests', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - slug: Config.engineSlug, - ); - - when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile()..filename = 'shell/platform/android/io/flutter/Blah.java', - PullRequestFile()..filename = 'shell/platform/android/test/io/flutter/BlahTest.java', - ]), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.addLabelsToIssue(Config.engineSlug, issueNumber, any), - ); - - verifyNever( - issuesService.createComment( - Config.engineSlug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Engine labels PRs, no comment if script tests', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - slug: Config.engineSlug, - ); - - when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile()..filename = 'fml/blah.cc', - PullRequestFile()..filename = 'fml/testing/blah_test.sh', - ]), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.createComment( - Config.engineSlug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Engine labels PRs, no comment if cc tests', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - slug: Config.engineSlug, - ); - - when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile()..filename = 'fml/blah.cc', - PullRequestFile()..filename = 'fml/blah_unittests.cc', - ]), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.addLabelsToIssue( - Config.engineSlug, - issueNumber, - any, - ), - ); - - verifyNever( - issuesService.createComment( - Config.engineSlug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Engine labels PRs, no comment if py tests', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - slug: Config.engineSlug, - ); - - when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile()..filename = 'tools/font-subset/main.cc', - PullRequestFile()..filename = 'tools/font-subset/test.py', - ]), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.addLabelsToIssue( - Config.engineSlug, - issueNumber, - any, - ), - ); - - verifyNever( - issuesService.createComment( - Config.engineSlug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Engine labels PRs, no comment if cc benchmarks', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - slug: Config.engineSlug, - ); - - when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile()..filename = 'fml/blah.cc', - PullRequestFile()..filename = 'fml/blah_benchmarks.cc', - ]), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.addLabelsToIssue( - Config.engineSlug, - issueNumber, - any, - ), - ); - - verifyNever( - issuesService.createComment( - Config.engineSlug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Engine labels PRs, no comments if pr is for release branches', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - baseRef: kReleaseBaseRef, - headRef: kReleaseHeadRef, - slug: Config.engineSlug, - ); - when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer( - (_) => Stream.value( - PullRequestFile()..filename = 'shell/platform/darwin/ios/framework/Source/boost.mm', - ), - ); - - when(issuesService.listCommentsByIssue(Config.engineSlug, issueNumber)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = 'some other comment', - ), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.createComment( - Config.engineSlug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('bot does not comment for whitespace only changes', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - slug: Config.engineSlug, - ); - const String patch = ''' -@@ -128,7 +128,7 @@ - - int bar = 0; -+ - int baz = 0; -'''; - - when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile() - ..filename = 'flutter/lib/ui/foo.dart' - ..additionsCount = 1 - ..deletionsCount = 1 - ..changesCount = 2 - ..patch = patch, - ]), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.createComment( - Config.engineSlug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Engine does not comment for comment-only changes', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - slug: Config.engineSlug, - ); - const String patch = ''' -@@ -128,7 +128,7 @@ - -/// Insert interesting comment here. -/// --/// More details here, but some of them are wrong. -+/// These are the right details! -void foo() { - int bar = 0; - String baz = ''; -'''; - - when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile() - ..filename = 'flutter/lib/ui/foo.dart' - ..additionsCount = 1 - ..deletionsCount = 1 - ..changesCount = 2 - ..patch = patch, - ]), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.createComment( - Config.engineSlug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Engine labels deletion only PR, no test request', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - slug: Config.engineSlug, - ); - - when(pullRequestsService.listFiles(Config.engineSlug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile() - ..filename = 'flutter/lib/ui/foo.dart' - ..deletionsCount = 20 - ..additionsCount = 0 - ..changesCount = 20, - ]), - ); - - await tester.post(webhook); - - // The PR here is only deleting code, so no test comment. - verifyNever( - issuesService.createComment( - Config.engineSlug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('No labels when only pubspec.yaml changes', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - ); - - when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile()..filename = 'packages/flutter/pubspec.yaml', - PullRequestFile()..filename = 'packages/flutter_tools/pubspec.yaml', - ]), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.createComment( - Config.flutterSlug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Packages does not comment if Pigeon native tests', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - slug: Config.packagesSlug, - baseRef: Config.defaultBranch(Config.packagesSlug), - ); - when(pullRequestsService.listFiles(Config.packagesSlug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile()..filename = 'packages/pigeon/lib/swift_generator.dart', - PullRequestFile() - ..filename = 'packages/pigeon/platform_tests/shared_test_plugin_code/lib/integration_tests.dart', - ]), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.createComment( - Config.packagesSlug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Packages does not comment if editing test files in go_router', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - slug: Config.packagesSlug, - baseRef: Config.defaultBranch(Config.packagesSlug), - ); - when(pullRequestsService.listFiles(Config.packagesSlug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile() - ..filename = 'packages/packages/go_router/test_fixes/go_router.dart' - ..additionsCount = 10, - PullRequestFile() - ..filename = 'packages/packages/go_router/lib/fix_data.yaml' - ..additionsCount = 10, - ]), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.createComment( - Config.packagesSlug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Packages does not comment if editing test files in go_router_builder', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - slug: Config.packagesSlug, - baseRef: Config.defaultBranch(Config.packagesSlug), - ); - when(pullRequestsService.listFiles(Config.packagesSlug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile() - ..filename = 'packages/packages/go_router_builder/lib/src/route_config.dart' - ..additionsCount = 10, - PullRequestFile() - ..filename = 'packages/packages/go_router_builder/test_inputs/bad_path_pattern.dart' - ..additionsCount = 10, - ]), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.createComment( - Config.packagesSlug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Packages comments and labels if no tests', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - slug: Config.packagesSlug, - baseRef: Config.defaultBranch(Config.packagesSlug), - ); - when(pullRequestsService.listFiles(Config.packagesSlug, issueNumber)).thenAnswer( - (_) => Stream.value( - PullRequestFile()..filename = 'packages/foo/lib/foo.dart', - ), - ); - - when(issuesService.listCommentsByIssue(Config.packagesSlug, issueNumber)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = 'some other comment', - ), - ); - - await tester.post(webhook); - - verify( - issuesService.createComment( - Config.packagesSlug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ).called(1); - }); - - test('Packages do not comment or label if pr is for release branches', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - baseRef: kReleaseBaseRef, - headRef: kReleaseHeadRef, - slug: Config.packagesSlug, - ); - - when(pullRequestsService.listFiles(Config.packagesSlug, issueNumber)).thenAnswer( - (_) => Stream.value( - PullRequestFile()..filename = 'packages/foo/lib/foo.dart', - ), - ); - - when(issuesService.listCommentsByIssue(Config.packagesSlug, issueNumber)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = 'some other comment', - ), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.createComment( - Config.packagesSlug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - - verifyNever( - issuesService.addLabelsToIssue( - Config.packagesSlug, - issueNumber, - any, - ), - ); - }); - - test('Packages does not comment if Dart tests', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - slug: Config.packagesSlug, - ); - - when(pullRequestsService.listFiles(Config.packagesSlug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile()..filename = 'packages/foo/lib/foo.dart', - PullRequestFile()..filename = 'packages/foo/test/foo_test.dart', - ]), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.createComment( - Config.packagesSlug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Packages does not comment for custom test driver', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - slug: Config.packagesSlug, - ); - - when(pullRequestsService.listFiles(Config.packagesSlug, issueNumber)).thenAnswer( - (_) => Stream.fromIterable([ - PullRequestFile()..filename = 'packages/foo/tool/run_tests.dart', - PullRequestFile()..filename = 'packages/foo/run_tests.sh', - ]), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.createComment( - Config.packagesSlug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Schedule tasks when pull request is closed and merged', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'closed', - number: issueNumber, - merged: true, - baseSha: 'sha1', // Found in pre-populated commits in FakeGerritService. - mergeCommitSha: 'sha2', - ); - - expect(db.values.values.whereType().length, 0); - await tester.post(webhook); - expect(db.values.values.whereType().length, 1); - }); - - test('Fail when pull request is closed and merged, but merged commit is not found on GoB', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'closed', - number: issueNumber, - merged: true, - baseSha: 'unknown_sha', - ); - - expect(db.values.values.whereType().length, 0); - try { - await tester.post(webhook); - } catch (e) { - expect( - e.toString(), - matches( - r'HTTP 500: (.+) was not found on GoB\. Failing so this event can be retried\.\.\.', - ), - ); - } - expect(db.values.values.whereType().length, 0); - }); - - test('Does not comment about needing tests on draft pull requests.', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - isDraft: true, - ); - - when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer( - (_) => Stream.value( - PullRequestFile()..filename = 'some_change.dart', - ), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.createComment( - Config.flutterSlug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Will not spawn comments if they have already been made.', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - ); - - when(pullRequestsService.listFiles(Config.flutterSlug, issueNumber)).thenAnswer( - (_) => Stream.value( - PullRequestFile()..filename = 'packages/flutter/blah.dart', - ), - ); - - when(issuesService.listCommentsByIssue(Config.flutterSlug, issueNumber)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = config.missingTestsPullRequestMessageValue, - ), - ); - - await tester.post(webhook); - - verifyNever( - issuesService.addLabelsToIssue( - Config.flutterSlug, - issueNumber, - any, - ), - ); - - verifyNever( - issuesService.createComment( - Config.flutterSlug, - issueNumber, - argThat(contains(config.missingTestsPullRequestMessageValue)), - ), - ); - }); - - test('Skips labeling or commenting on autorolls', () async { - const int issueNumber = 123; - - tester.message = generateGithubWebhookMessage( - action: 'opened', - number: issueNumber, - login: 'engine-flutter-autoroll', - ); - - await tester.post(webhook); - - verifyNever( - issuesService.createComment( - any, - issueNumber, - any, - ), - ); - }); - - test('Comments on PR but does not schedule builds for unmergeable PRs', () async { - const int issueNumber = 12345; - tester.message = generateGithubWebhookMessage( - action: 'synchronize', - number: issueNumber, - // This PR is unmergeable (probably merge conflict) - mergeable: false, - ); - - await tester.post(webhook); - verify( - issuesService.createComment( - Config.flutterSlug, - issueNumber, - config.mergeConflictPullRequestMessage, - ), - ); - }); - - test('When synchronized, cancels existing builds and schedules new ones', () async { - const int issueNumber = 12345; - bool batchRequestCalled = false; - Future getBatchResponse() async { - batchRequestCalled = true; - return BatchResponse( - responses: [ - Response( - searchBuilds: SearchBuildsResponse( - builds: [ - generateBuild(999, name: 'Linux', status: Status.ended), - ], - ), - ), - Response( - searchBuilds: SearchBuildsResponse( - builds: [ - generateBuild(998, name: 'Linux', status: Status.ended), - ], - ), - ), - ], - ); - } - - fakeBuildBucketClient.batchResponse = getBatchResponse; - - tester.message = generateGithubWebhookMessage( - action: 'synchronize', - number: issueNumber, - ); - - final MockRepositoriesService mockRepositoriesService = MockRepositoriesService(); - when(gitHubClient.repositories).thenReturn(mockRepositoriesService); - - await tester.post(webhook); - expect(batchRequestCalled, isTrue); - }); - - group('BuildBucket', () { - const int issueNumber = 123; - - Future testActions(String action) async { - when(issuesService.listLabelsByIssue(any, issueNumber)).thenAnswer((_) { - return Stream.fromIterable([ - IssueLabel()..name = 'Random Label', - ]); - }); - - fakeBuildBucketClient.batchResponse = () => Future.value( - const BatchResponse( - responses: [ - Response( - searchBuilds: SearchBuildsResponse( - builds: [], - ), - ), - Response( - searchBuilds: SearchBuildsResponse( - builds: [], - ), - ), - ], - ), - ); - - tester.message = generateGithubWebhookMessage( - action: action, - number: 1, - ); - - await tester.post(webhook); - } - - test('Edited Action works properly', () async { - await testActions('edited'); - }); - - test('Opened Action works properly', () async { - await testActions('opened'); - }); - - test('Ready_for_review Action works properly', () async { - await testActions('ready_for_review'); - }); - - test('Reopened Action works properly', () async { - await testActions('reopened'); - }); - - test('Labeled Action works properly', () async { - await testActions('labeled'); - }); - - test('Synchronize Action works properly', () async { - await testActions('synchronize'); - }); - - test('Comments on PR but does not schedule builds for unmergeable PRs', () async { - when(issuesService.listCommentsByIssue(any, any)).thenAnswer((_) => Stream.value(IssueComment())); - tester.message = generateGithubWebhookMessage( - action: 'synchronize', - number: issueNumber, - // This PR is unmergeable (probably merge conflict) - mergeable: false, - ); - await tester.post(webhook); - verify( - issuesService.createComment( - Config.flutterSlug, - issueNumber, - config.mergeConflictPullRequestMessage, - ), - ); - }); - - test('When synchronized, cancels existing builds and schedules new ones', () async { - fakeBuildBucketClient.batchResponse = () => Future.value( - BatchResponse( - responses: [ - Response( - searchBuilds: SearchBuildsResponse( - builds: [ - generateBuild(999, name: 'Linux', status: Status.ended), - ], - ), - ), - Response( - searchBuilds: SearchBuildsResponse( - builds: [ - generateBuild(998, name: 'Linux', status: Status.ended), - ], - ), - ), - ], - ), - ); - - tester.message = generateGithubWebhookMessage( - action: 'synchronize', - number: issueNumber, - ); - final MockRepositoriesService mockRepositoriesService = MockRepositoriesService(); - when(gitHubClient.repositories).thenReturn(mockRepositoriesService); - - await tester.post(webhook); - }); - }); - }); - - group('github webhook check_run event', () { - test('processes check run event', () async { - tester.message = generateCheckRunEvent(); - - await tester.post(webhook); - }); - - test('processes completed check run event', () async { - tester.message = generateCheckRunEvent( - action: 'completed', - numberOfPullRequests: 0, - ); - - await tester.post(webhook); - }); - }); - - group('github webhook push event', () { - test('handles push events for flutter/flutter beta branch', () async { - tester.message = generatePushMessage('beta', 'flutter', 'flutter'); - - await tester.post(webhook); - - verify(commitService.handlePushGithubRequest(any)).called(1); - }); - - test('handles push events for flutter/flutter stable branch', () async { - tester.message = generatePushMessage('stable', 'flutter', 'flutter'); - - await tester.post(webhook); - - verify(commitService.handlePushGithubRequest(any)).called(1); - }); - - test('does not handle push events for branches that are not beta|stable', () async { - tester.message = generatePushMessage('main', 'flutter', 'flutter'); - - await tester.post(webhook); - - verifyNever(commitService.handlePushGithubRequest(any)).called(0); - }); - - test('does not handle push events for repositories that are not flutter/flutter', () async { - tester.message = generatePushMessage('beta', 'flutter', 'engine'); - - await tester.post(webhook); - - verifyNever(commitService.handlePushGithubRequest(any)).called(0); - }); - }); - - group('github webhook create event', () { - test('Does not create a new commit due to not being a candidate branch', () async { - tester.message = generateCreateBranchMessage( - 'cool-branch', - 'flutter/flutter', - ); - - await tester.post(webhook); - - verifyNever(commitService.handleCreateGithubRequest(any)).called(0); - }); - - test('Creates a new commit due to being a candidate branch', () async { - tester.message = generateCreateBranchMessage( - 'flutter-1.2-candidate.3', - 'flutter/flutter', - ); - - await tester.post(webhook); - - verify(commitService.handleCreateGithubRequest(any)).called(1); - }); - }); -} diff --git a/app_dart/test/request_handlers/github_webhook_test.dart b/app_dart/test/request_handlers/github_webhook_test.dart deleted file mode 100644 index dceeaa8b0..000000000 --- a/app_dart/test/request_handlers/github_webhook_test.dart +++ /dev/null @@ -1,83 +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:cocoon_service/cocoon_service.dart'; -import 'package:cocoon_service/protos.dart'; -import 'package:cocoon_service/src/request_handling/exceptions.dart'; - -import 'package:crypto/crypto.dart'; -import 'package:test/test.dart'; - -import '../src/datastore/fake_config.dart'; -import '../src/request_handling/fake_http.dart'; -import '../src/request_handling/fake_pubsub.dart'; -import '../src/request_handling/request_handler_tester.dart'; - -void main() { - late GithubWebhook webhook; - late FakeConfig config; - late FakeHttpRequest request; - late FakePubSub pubsub; - late RequestHandlerTester tester; - const String keyString = 'not_a_real_key'; - - String getHmac(Uint8List list, Uint8List key) { - final Hmac hmac = Hmac(sha1, key); - return hmac.convert(list).toString(); - } - - setUp(() { - request = FakeHttpRequest(); - tester = RequestHandlerTester(request: request); - - config = FakeConfig( - webhookKeyValue: keyString, - ); - pubsub = FakePubSub(); - - webhook = GithubWebhook( - config: config, - pubsub: pubsub, - secret: config.webhookKey, - topic: 'github-webhooks', - ); - }); - - test('Rejects non-POST methods with methodNotAllowed', () async { - expect(tester.get(webhook), throwsA(isA())); - expect(pubsub.messages, isEmpty); - }); - - test('Rejects missing headers', () async { - expect(tester.post(webhook), throwsA(isA())); - expect(pubsub.messages, isEmpty); - }); - - test('Rejects invalid hmac', () async { - request.headers.set('X-GitHub-Event', 'pull_request'); - request.headers.set('X-Hub-Signature', 'bar'); - request.body = 'Hello, World!'; - expect(tester.post(webhook), throwsA(isA())); - expect(pubsub.messages, isEmpty); - }); - - test('Publishes message', () async { - request.headers.set('X-GitHub-Event', 'pull_request'); - request.body = '{}'; - final Uint8List body = utf8.encode(request.body!); - final Uint8List key = utf8.encode(keyString); - final String hmac = getHmac(body, key); - request.headers.set('X-Hub-Signature', 'sha1=$hmac'); - await tester.post(webhook); - - expect(pubsub.messages, hasLength(1)); - final Map messageJson = pubsub.messages.single; - final GithubWebhookMessage message = GithubWebhookMessage.fromJson(jsonEncode(messageJson)); - expect(message.event, 'pull_request'); - expect(message.payload, '{}'); - }); -} diff --git a/app_dart/test/request_handlers/postsubmit_luci_subscription_test.dart b/app_dart/test/request_handlers/postsubmit_luci_subscription_test.dart deleted file mode 100644 index 17ef64890..000000000 --- a/app_dart/test/request_handlers/postsubmit_luci_subscription_test.dart +++ /dev/null @@ -1,357 +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/cocoon_service.dart'; -import 'package:cocoon_service/src/model/appengine/commit.dart'; -import 'package:cocoon_service/src/model/appengine/task.dart'; -import 'package:cocoon_service/src/request_handling/exceptions.dart'; -import 'package:cocoon_service/src/service/datastore.dart'; -import 'package:gcloud/db.dart'; -import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; - -import '../src/datastore/fake_config.dart'; -import '../src/request_handling/fake_authentication.dart'; -import '../src/request_handling/fake_http.dart'; -import '../src/request_handling/subscription_tester.dart'; -import '../src/service/fake_luci_build_service.dart'; -import '../src/service/fake_scheduler.dart'; -import '../src/utilities/entity_generators.dart'; -import '../src/utilities/mocks.dart'; -import '../src/utilities/push_message.dart'; - -void main() { - late PostsubmitLuciSubscription handler; - late FakeConfig config; - late FakeHttpRequest request; - late SubscriptionTester tester; - late MockGithubChecksService mockGithubChecksService; - late MockGithubChecksUtil mockGithubChecksUtil; - late FakeScheduler scheduler; - - setUp(() async { - config = FakeConfig(maxLuciTaskRetriesValue: 3); - mockGithubChecksUtil = MockGithubChecksUtil(); - mockGithubChecksService = MockGithubChecksService(); - when(mockGithubChecksService.githubChecksUtil).thenReturn(mockGithubChecksUtil); - when(mockGithubChecksUtil.createCheckRun(any, any, any, any, output: anyNamed('output'))) - .thenAnswer((_) async => generateCheckRun(1, name: 'Linux A')); - when(mockGithubChecksService.updateCheckStatus(any, any, any)).thenAnswer((_) async => true); - final FakeLuciBuildService luciBuildService = FakeLuciBuildService( - config: config, - githubChecksUtil: mockGithubChecksUtil, - ); - scheduler = FakeScheduler( - ciYaml: exampleConfig, - config: config, - luciBuildService: luciBuildService, - ); - handler = PostsubmitLuciSubscription( - cache: CacheService(inMemory: true), - config: config, - authProvider: FakeAuthenticationProvider(), - githubChecksService: mockGithubChecksService, - datastoreProvider: (_) => DatastoreService(config.db, 5), - scheduler: scheduler, - ); - request = FakeHttpRequest(); - - tester = SubscriptionTester( - request: request, - ); - }); - - test('throws exception when task key is not in message', () async { - tester.message = createBuildbucketPushMessage( - 'COMPLETED', - result: 'SUCCESS', - builderName: '', - userData: '{\\"commit_key\\":\\"flutter/main/abc123\\"}', - ); - - expect(() => tester.post(handler), throwsA(isA())); - }); - - test('throws exception if task key does not exist in datastore', () { - final Task task = generateTask(1); - tester.message = createBuildbucketPushMessage( - 'COMPLETED', - result: 'SUCCESS', - userData: '{\\"task_key\\":\\"${task.key.id}\\", \\"commit_key\\":\\"${task.key.parent?.id}\\"}', - ); - - expect(() => tester.post(handler), throwsA(isA())); - }); - - test('updates task based on message', () async { - final Commit commit = generateCommit(1, sha: '87f88734747805589f2131753620d61b22922822'); - final Task task = generateTask( - 4507531199512576, - parent: commit, - name: 'Linux A', - ); - - tester.message = createBuildbucketPushMessage( - 'COMPLETED', - result: 'SUCCESS', - userData: '{\\"task_key\\":\\"${task.key.id}\\", \\"commit_key\\":\\"${task.key.parent?.id}\\"}', - ); - - config.db.values[commit.key] = commit; - config.db.values[task.key] = task; - - expect(task.status, Task.statusNew); - expect(task.endTimestamp, 0); - - await tester.post(handler); - - expect(task.status, Task.statusSucceeded); - expect(task.endTimestamp, 1565049193786); - }); - - test('skips task processing when build is with scheduled status', () async { - final Commit commit = generateCommit(1, sha: '87f88734747805589f2131753620d61b22922822'); - final Task task = generateTask( - 4507531199512576, - name: 'Linux A', - parent: commit, - status: Task.statusInProgress, - ); - config.db.values[task.key] = task; - config.db.values[commit.key] = commit; - tester.message = createBuildbucketPushMessage( - 'SCHEDULED', - builderName: 'Linux A', - result: null, - userData: '{\\"task_key\\":\\"${task.key.id}\\", \\"commit_key\\":\\"${task.key.parent?.id}\\"}', - ); - - expect(task.status, Task.statusInProgress); - expect(task.attempts, 1); - expect(await tester.post(handler), Body.empty); - expect(task.status, Task.statusInProgress); - }); - - test('skips task processing when task has already finished', () async { - final Commit commit = generateCommit(1, sha: '87f88734747805589f2131753620d61b22922822'); - final Task task = generateTask( - 4507531199512576, - name: 'Linux A', - parent: commit, - status: Task.statusSucceeded, - ); - config.db.values[task.key] = task; - config.db.values[commit.key] = commit; - tester.message = createBuildbucketPushMessage( - 'STARTED', - builderName: 'Linux A', - result: null, - userData: '{\\"task_key\\":\\"${task.key.id}\\", \\"commit_key\\":\\"${task.key.parent?.id}\\"}', - ); - - expect(task.status, Task.statusSucceeded); - expect(task.attempts, 1); - expect(await tester.post(handler), Body.empty); - expect(task.status, Task.statusSucceeded); - }); - - test('skips task processing when target has been deleted', () async { - final Commit commit = generateCommit(1, sha: '87f88734747805589f2131753620d61b22922822'); - final Task task = generateTask( - 4507531199512576, - name: 'Linux B', - parent: commit, - status: Task.statusSucceeded, - ); - config.db.values[task.key] = task; - config.db.values[commit.key] = commit; - tester.message = createBuildbucketPushMessage( - 'STARTED', - builderName: 'Linux B', - result: null, - userData: '{\\"task_key\\":\\"${task.key.id}\\", \\"commit_key\\":\\"${task.key.parent?.id}\\"}', - ); - - expect(task.status, Task.statusSucceeded); - expect(task.attempts, 1); - expect(await tester.post(handler), Body.empty); - }); - - test('does not fail on empty user data', () async { - tester.message = createBuildbucketPushMessage( - 'COMPLETED', - result: 'SUCCESS', - userData: null, - ); - - expect(await tester.post(handler), Body.empty); - }); - - test('on failed builds auto-rerun the build', () async { - final Commit commit = generateCommit(1, sha: '87f88734747805589f2131753620d61b22922822'); - final Task task = generateTask( - 4507531199512576, - name: 'Linux A', - parent: commit, - status: Task.statusFailed, - ); - config.db.values[task.key] = task; - config.db.values[commit.key] = commit; - tester.message = createBuildbucketPushMessage( - 'COMPLETED', - builderName: 'Linux A', - result: 'FAILURE', - userData: '{\\"task_key\\":\\"${task.key.id}\\", \\"commit_key\\":\\"${task.key.parent?.id}\\"}', - ); - - expect(task.status, Task.statusFailed); - expect(task.attempts, 1); - expect(await tester.post(handler), Body.empty); - expect(task.status, Task.statusInProgress); - expect(task.attempts, 2); - }); - - test('on canceled builds auto-rerun the build if they timed out', () async { - final Commit commit = generateCommit(1, sha: '87f88734747805589f2131753620d61b22922822'); - final Task task = generateTask( - 4507531199512576, - name: 'Linux A', - parent: commit, - status: Task.statusInfraFailure, - ); - config.db.values[task.key] = task; - config.db.values[commit.key] = commit; - tester.message = createBuildbucketPushMessage( - 'COMPLETED', - builderName: 'Linux A', - result: 'CANCELED', - userData: '{\\"task_key\\":\\"${task.key.id}\\", \\"commit_key\\":\\"${task.key.parent?.id}\\"}', - ); - - expect(task.status, Task.statusInfraFailure); - expect(task.attempts, 1); - expect(await tester.post(handler), Body.empty); - expect(task.status, Task.statusInProgress); - expect(task.attempts, 2); - }); - - test('on builds resulting in an infra failure auto-rerun the build if they timed out', () async { - final Commit commit = generateCommit(1, sha: '87f88734747805589f2131753620d61b22922822'); - final Task task = generateTask( - 4507531199512576, - name: 'Linux A', - parent: commit, - status: Task.statusInfraFailure, - ); - config.db.values[task.key] = task; - config.db.values[commit.key] = commit; - tester.message = createBuildbucketPushMessage( - 'COMPLETED', - builderName: 'Linux A', - result: 'FAILURE', - failureReason: 'INFRA_FAILURE', - userData: '{\\"task_key\\":\\"${task.key.id}\\", \\"commit_key\\":\\"${task.key.parent?.id}\\"}', - ); - - expect(task.status, Task.statusInfraFailure); - expect(task.attempts, 1); - expect(await tester.post(handler), Body.empty); - expect(task.status, Task.statusInProgress); - expect(task.attempts, 2); - }); - - test('fallback to build parameters if task_key is not present', () async { - final Commit commit = generateCommit(1, sha: '87f88734747805589f2131753620d61b22922822'); - final Task task = generateTask( - 4507531199512576, - name: 'Linux A', - parent: commit, - status: Task.statusNew, - ); - config.db.values[task.key] = task; - config.db.values[commit.key] = commit; - tester.message = createBuildbucketPushMessage( - 'COMPLETED', - builderName: 'Linux A', - result: 'FAILURE', - userData: '{\\"task_key\\":\\"null\\", \\"commit_key\\":\\"${task.key.parent?.id}\\"}', - ); - - expect(task.status, Task.statusNew); - expect(await tester.post(handler), Body.empty); - expect(task.status, Task.statusInProgress); - }); - - test('non-bringup target updates check run', () async { - scheduler.ciYaml = nonBringupPackagesConfig; - when(mockGithubChecksService.updateCheckStatus(any, any, any)).thenAnswer((_) async => true); - final Commit commit = generateCommit(1, sha: '87f88734747805589f2131753620d61b22922822', repo: 'packages'); - final Task task = generateTask( - 4507531199512576, - name: 'Linux nonbringup', - parent: commit, - ); - config.db.values[commit.key] = commit; - config.db.values[task.key] = task; - - tester.message = createBuildbucketPushMessage( - 'COMPLETED', - result: 'SUCCESS', - builderName: 'Linux A', - // Use escaped string to mock json decoded ones. - userData: - '{\\"task_key\\":\\"${task.key.id}\\", \\"commit_key\\":\\"${task.key.parent?.id}\\", \\"repo_owner\\": \\"flutter\\", \\"repo_name\\": \\"packages\\"}', - ); - await tester.post(handler); - verify(mockGithubChecksService.updateCheckStatus(any, any, any)).called(1); - }); - - test('bringup target does not update check run', () async { - scheduler.ciYaml = bringupPackagesConfig; - when(mockGithubChecksService.updateCheckStatus(any, any, any)).thenAnswer((_) async => true); - final Commit commit = generateCommit(1, sha: '87f88734747805589f2131753620d61b22922822'); - final Task task = generateTask( - 4507531199512576, - name: 'Linux bringup', - parent: commit, - ); - config.db.values[commit.key] = commit; - config.db.values[task.key] = task; - - tester.message = createBuildbucketPushMessage( - 'COMPLETED', - result: 'SUCCESS', - builderName: 'Linux bringup', - // Use escaped string to mock json decoded ones. - userData: - '{\\"task_key\\":\\"${task.key.id}\\", \\"commit_key\\":\\"${task.key.parent?.id}\\", \\"repo_owner\\": \\"flutter\\", \\"repo_name\\": \\"packages\\"}', - ); - await tester.post(handler); - verifyNever(mockGithubChecksService.updateCheckStatus(any, any, any)); - }); - - test('unsupported repo target does not update check run', () async { - scheduler.ciYaml = unsupportedPostsubmitCheckrunConfig; - when(mockGithubChecksService.updateCheckStatus(any, any, any)).thenAnswer((_) async => true); - final Commit commit = generateCommit(1, sha: '87f88734747805589f2131753620d61b22922822'); - final Task task = generateTask( - 4507531199512576, - name: 'Linux flutter', - parent: commit, - ); - config.db.values[commit.key] = commit; - config.db.values[task.key] = task; - - tester.message = createBuildbucketPushMessage( - 'COMPLETED', - result: 'SUCCESS', - builderName: 'Linux bringup', - // Use escaped string to mock json decoded ones. - userData: - '{\\"task_key\\":\\"${task.key.id}\\", \\"commit_key\\":\\"${task.key.parent?.id}\\", \\"repo_owner\\": \\"flutter\\", \\"repo_name\\": \\"flutter\\"}', - ); - await tester.post(handler); - verifyNever(mockGithubChecksService.updateCheckStatus(any, any, any)); - }); -} diff --git a/app_dart/test/request_handlers/presubmit_luci_subscription_test.dart b/app_dart/test/request_handlers/presubmit_luci_subscription_test.dart deleted file mode 100644 index 87e7cb735..000000000 --- a/app_dart/test/request_handlers/presubmit_luci_subscription_test.dart +++ /dev/null @@ -1,186 +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/cocoon_service.dart'; -import 'package:cocoon_service/src/model/luci/buildbucket.dart' as bb; -import 'package:cocoon_service/src/model/luci/push_message.dart'; -import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; - -import '../src/datastore/fake_config.dart'; -import '../src/request_handling/fake_authentication.dart'; -import '../src/request_handling/fake_http.dart'; -import '../src/request_handling/subscription_tester.dart'; -import '../src/service/fake_buildbucket.dart'; -import '../src/service/fake_luci_build_service.dart'; -import '../src/service/fake_scheduler.dart'; -import '../src/utilities/mocks.dart'; -import '../src/utilities/push_message.dart'; - -const String ref = 'deadbeef'; - -void main() { - late PresubmitLuciSubscription handler; - late FakeBuildBucketClient buildbucket; - late FakeConfig config; - late MockGitHub mockGitHubClient; - late FakeHttpRequest request; - late SubscriptionTester tester; - late MockRepositoriesService mockRepositoriesService; - late MockGithubChecksService mockGithubChecksService; - late MockLuciBuildService mockLuciBuildService; - late FakeScheduler scheduler; - - setUp(() async { - config = FakeConfig(); - buildbucket = FakeBuildBucketClient(); - mockLuciBuildService = MockLuciBuildService(); - - mockGithubChecksService = MockGithubChecksService(); - scheduler = FakeScheduler( - ciYaml: examplePresubmitRescheduleConfig, - config: config, - luciBuildService: mockLuciBuildService, - ); - handler = PresubmitLuciSubscription( - cache: CacheService(inMemory: true), - config: config, - buildBucketClient: buildbucket, - luciBuildService: FakeLuciBuildService(config: config), - githubChecksService: mockGithubChecksService, - authProvider: FakeAuthenticationProvider(), - scheduler: scheduler, - ); - request = FakeHttpRequest(); - - tester = SubscriptionTester( - request: request, - ); - - mockGitHubClient = MockGitHub(); - mockRepositoriesService = MockRepositoriesService(); - when(mockGitHubClient.repositories).thenReturn(mockRepositoriesService); - config.githubClient = mockGitHubClient; - }); - - test('Requests without repo_owner and repo_name do not update checks', () async { - tester.message = pushMessageJsonNoBuildset( - 'COMPLETED', - result: 'SUCCESS', - builderName: 'Linux Host Engine', - ); - - await tester.post(handler); - verifyNever(mockGithubChecksService.updateCheckStatus(any, any, any)); - }); - - test('Requests with repo_owner and repo_name update checks', () async { - when(mockGithubChecksService.updateCheckStatus(any, any, any)).thenAnswer((_) async => true); - when(mockGithubChecksService.taskFailed(any)).thenAnswer((_) => false); - tester.message = createBuildbucketPushMessage( - 'COMPLETED', - result: 'SUCCESS', - builderName: 'Linux Host Engine', - userData: '{\\"repo_owner\\": \\"flutter\\", \\"repo_name\\": \\"cocoon\\"}', - ); - await tester.post(handler); - verify(mockGithubChecksService.updateCheckStatus(any, any, any)).called(1); - }); - - test('Requests when task failed but no need to reschedule', () async { - when(mockGithubChecksService.updateCheckStatus(any, any, any)).thenAnswer((_) async => true); - when(mockGithubChecksService.taskFailed(any)).thenAnswer((_) => true); - when(mockGithubChecksService.currentAttempt(any)).thenAnswer((_) => 1); - tester.message = createBuildbucketPushMessage( - 'COMPLETED', - result: 'SUCCESS', - builderName: 'Linux A', - userData: '{\\"repo_owner\\": \\"flutter\\",' - '\\"commit_branch\\": \\"main\\",' - '\\"commit_sha\\": \\"abc\\",' - '\\"repo_name\\": \\"flutter\\"}', - ); - when( - mockLuciBuildService.rescheduleBuild( - builderName: 'Linux Coverage', - buildPushMessage: BuildPushMessage.fromPushMessage(tester.message), - rescheduleAttempt: 0, - ), - ).thenAnswer( - (_) async => const bb.Build( - id: '8905920700440101120', - builderId: bb.BuilderId(bucket: 'luci.flutter.prod', project: 'flutter', builder: 'Linux Coverage'), - ), - ); - await tester.post(handler); - verifyNever( - mockLuciBuildService.rescheduleBuild( - builderName: 'Linux Coverage', - buildPushMessage: BuildPushMessage.fromPushMessage(tester.message), - rescheduleAttempt: 0, - ), - ); - verify(mockGithubChecksService.updateCheckStatus(any, any, any)).called(1); - }); - test('Requests when task failed but need to reschedule', () async { - when(mockGithubChecksService.updateCheckStatus(any, any, any, rescheduled: true)).thenAnswer((_) async => true); - when(mockGithubChecksService.taskFailed(any)).thenAnswer((_) => true); - when(mockGithubChecksService.currentAttempt(any)).thenAnswer((_) => 0); - tester.message = createBuildbucketPushMessage( - 'COMPLETED', - result: 'SUCCESS', - builderName: 'Linux B', - userData: '{\\"repo_owner\\": \\"flutter\\",' - '\\"commit_branch\\": \\"main\\",' - '\\"commit_sha\\": \\"abc\\",' - '\\"repo_name\\": \\"flutter\\"}', - ); - when( - mockLuciBuildService.rescheduleBuild( - builderName: 'Linux Coverage', - buildPushMessage: BuildPushMessage.fromPushMessage(tester.message), - rescheduleAttempt: 1, - ), - ).thenAnswer( - (_) async => const bb.Build( - id: '8905920700440101120', - builderId: bb.BuilderId(bucket: 'luci.flutter.prod', project: 'flutter', builder: 'Linux B'), - ), - ); - await tester.post(handler); - verifyNever( - mockLuciBuildService.rescheduleBuild( - builderName: 'Linux B', - buildPushMessage: BuildPushMessage.fromPushMessage(tester.message), - rescheduleAttempt: 1, - ), - ); - verify(mockGithubChecksService.updateCheckStatus(any, any, any, rescheduled: true)).called(1); - }); - - test('Build not rescheduled if not found in ciYaml list.', () async { - when(mockGithubChecksService.updateCheckStatus(any, any, any, rescheduled: false)).thenAnswer((_) async => true); - when(mockGithubChecksService.taskFailed(any)).thenAnswer((_) => true); - when(mockGithubChecksService.currentAttempt(any)).thenAnswer((_) => 1); - tester.message = createBuildbucketPushMessage( - 'COMPLETED', - result: 'SUCCESS', - // This builder will not be present. - builderName: 'Linux C', - userData: '{\\"repo_owner\\": \\"flutter\\",' - '\\"commit_branch\\": \\"main\\",' - '\\"commit_sha\\": \\"abc\\",' - '\\"repo_name\\": \\"flutter\\"}', - ); - await tester.post(handler); - verifyNever( - mockLuciBuildService.rescheduleBuild( - builderName: 'Linux C', - buildPushMessage: BuildPushMessage.fromPushMessage(tester.message), - rescheduleAttempt: 1, - ), - ); - verify(mockGithubChecksService.updateCheckStatus(any, any, any, rescheduled: false)).called(1); - }); -} diff --git a/app_dart/test/request_handlers/push_build_status_to_github_test.dart b/app_dart/test/request_handlers/push_build_status_to_github_test.dart deleted file mode 100644 index 17eff2fc5..000000000 --- a/app_dart/test/request_handlers/push_build_status_to_github_test.dart +++ /dev/null @@ -1,169 +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/model/appengine/github_build_status_update.dart'; -import 'package:cocoon_service/src/request_handlers/push_build_status_to_github.dart'; -import 'package:cocoon_service/src/request_handling/body.dart'; -import 'package:cocoon_service/src/service/build_status_provider.dart'; -import 'package:cocoon_service/src/service/config.dart'; -import 'package:cocoon_service/src/service/datastore.dart'; -import 'package:gcloud/db.dart'; -import 'package:github/github.dart'; -import 'package:googleapis/bigquery/v2.dart' hide Model; -import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; - -import '../src/bigquery/fake_tabledata_resource.dart'; -import '../src/datastore/fake_config.dart'; -import '../src/datastore/fake_datastore.dart'; -import '../src/request_handling/api_request_handler_tester.dart'; -import '../src/request_handling/fake_authentication.dart'; -import '../src/service/fake_build_status_provider.dart'; -import '../src/service/fake_github_service.dart'; -import '../src/utilities/entity_generators.dart'; -import '../src/utilities/mocks.dart'; - -void main() { - group('PushStatusToGithub', () { - late FakeBuildStatusService buildStatusService; - late FakeClientContext clientContext; - late FakeConfig config; - late FakeDatastoreDB db; - late ApiRequestHandlerTester tester; - late FakeAuthenticatedContext authContext; - late FakeTabledataResource tabledataResourceApi; - late PushBuildStatusToGithub handler; - late MockGitHub github; - late MockPullRequestsService pullRequestsService; - late MockIssuesService issuesService; - late MockRepositoriesService repositoriesService; - late FakeGithubService githubService; - - GithubBuildStatusUpdate newStatusUpdate(PullRequest pr, BuildStatus status) { - return GithubBuildStatusUpdate( - key: db.emptyKey.append(GithubBuildStatusUpdate, id: pr.number), - repository: Config.flutterSlug.fullName, - status: status.githubStatus, - pr: pr.number!, - head: pr.head!.sha, - updates: 0, - ); - } - - setUp(() async { - clientContext = FakeClientContext(); - authContext = FakeAuthenticatedContext(clientContext: clientContext); - clientContext.isDevelopmentEnvironment = false; - buildStatusService = FakeBuildStatusService(); - githubService = FakeGithubService(); - tabledataResourceApi = FakeTabledataResource(); - db = FakeDatastoreDB(); - github = MockGitHub(); - pullRequestsService = MockPullRequestsService(); - issuesService = MockIssuesService(); - repositoriesService = MockRepositoriesService(); - config = FakeConfig( - tabledataResource: tabledataResourceApi, - githubService: githubService, - dbValue: db, - githubClient: github, - ); - tester = ApiRequestHandlerTester(context: authContext); - handler = PushBuildStatusToGithub( - config: config, - authenticationProvider: FakeAuthenticationProvider(clientContext: clientContext), - buildStatusServiceProvider: (_) => buildStatusService, - datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5), - ); - - when(github.pullRequests).thenReturn(pullRequestsService); - when(github.issues).thenReturn(issuesService); - when(github.repositories).thenReturn(repositoriesService); - when(repositoriesService.createStatus(any, any, any)).thenAnswer( - (_) async => RepositoryStatus(), - ); - }); - - test('development environment does nothing', () async { - clientContext.isDevelopmentEnvironment = true; - config.githubClient = ThrowingGitHub(); - db.onCommit = (List> insert, List> deletes) => throw AssertionError(); - db.addOnQuery((Iterable results) { - throw AssertionError(); - }); - final Body body = await tester.get(handler); - expect(body, same(Body.empty)); - }); - - group('does not update anything', () { - setUp(() { - db.onCommit = (List> insert, List> deletes) => throw AssertionError(); - when(repositoriesService.createStatus(any, any, any)).thenThrow(AssertionError()); - }); - - test('if there are no PRs', () async { - when(pullRequestsService.list(any, base: anyNamed('base'))) - .thenAnswer((_) => const Stream.empty()); - buildStatusService.cumulativeStatus = BuildStatus.success(); - final Body body = await tester.get(handler); - final TableDataList tableDataList = await tabledataResourceApi.list('test', 'test', 'test'); - expect(body, same(Body.empty)); - - // Test for BigQuery insert - expect(tableDataList.totalRows, '1'); - }); - - test('only if pull request is for the default branch', () async { - when(pullRequestsService.list(any)).thenAnswer( - (_) => Stream.value( - generatePullRequest( - id: 1, - branch: 'flutter-2.15-candidate.3', - ), - ), - ); - buildStatusService.cumulativeStatus = BuildStatus.success(); - await tester.get(handler); - verifyNever(repositoriesService.createStatus(any, any, any)); - }); - - test('if status has not changed since last update', () async { - final PullRequest pr = generatePullRequest(id: 1, sha: '1'); - when(pullRequestsService.list(any, base: anyNamed('base'))).thenAnswer((_) => Stream.value(pr)); - buildStatusService.cumulativeStatus = BuildStatus.success(); - final GithubBuildStatusUpdate status = newStatusUpdate(pr, BuildStatus.success()); - db.values[status.key] = status; - final Body body = await tester.get(handler); - expect(body, same(Body.empty)); - expect(status.updates, 0); - }); - - test('if there is no pr found for a targeted branch', () async { - final PullRequest pr = generatePullRequest(id: 1, sha: '1'); - when(pullRequestsService.list(any, base: anyNamed('base'))).thenAnswer((_) => Stream.value(pr)); - buildStatusService.cumulativeStatus = BuildStatus.success(); - final GithubBuildStatusUpdate status = - newStatusUpdate(pr, BuildStatus.failure(const ['failed_task_1'])); - db.values[status.key] = status; - final Body body = await tester.get(handler); - expect(body, same(Body.empty)); - expect(status.updates, 0); - expect(status.status, BuildStatus.failure().githubStatus); - }); - }); - - test('updates github and datastore if status has changed since last update', () async { - final PullRequest pr = generatePullRequest(id: 1, sha: '1'); - when(pullRequestsService.list(any, base: anyNamed('base'))).thenAnswer((_) => Stream.value(pr)); - buildStatusService.cumulativeStatus = BuildStatus.success(); - final GithubBuildStatusUpdate status = newStatusUpdate(pr, BuildStatus.failure(const ['failed_test_1'])); - db.values[status.key] = status; - final Body body = await tester.get(handler); - expect(body, same(Body.empty)); - expect(status.updates, 1); - expect(status.updateTimeMillis, isNotNull); - expect(status.status, BuildStatus.success().githubStatus); - }); - }); -} diff --git a/app_dart/test/request_handlers/push_gold_status_to_github_test.dart b/app_dart/test/request_handlers/push_gold_status_to_github_test.dart deleted file mode 100644 index 80eb75882..000000000 --- a/app_dart/test/request_handlers/push_gold_status_to_github_test.dart +++ /dev/null @@ -1,1604 +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:io'; - -import 'package:cocoon_service/src/model/appengine/github_gold_status_update.dart'; -import 'package:cocoon_service/src/request_handlers/push_gold_status_to_github.dart'; -import 'package:cocoon_service/src/request_handling/body.dart'; -import 'package:cocoon_service/src/service/datastore.dart'; -import 'package:cocoon_service/src/service/logging.dart'; -import 'package:gcloud/db.dart' as gcloud_db; -import 'package:gcloud/db.dart'; -import 'package:github/github.dart'; -import 'package:graphql/client.dart'; -import 'package:http/http.dart' as http; -import 'package:http/testing.dart'; -import 'package:logging/logging.dart'; -import 'package:mockito/mockito.dart'; -import 'package:retry/retry.dart'; -import 'package:test/test.dart'; - -import '../src/datastore/fake_config.dart'; -import '../src/datastore/fake_datastore.dart'; -import '../src/request_handling/api_request_handler_tester.dart'; -import '../src/request_handling/fake_authentication.dart'; -import '../src/service/fake_graphql_client.dart'; -import '../src/utilities/mocks.dart'; - -void main() { - const String kGoldenFileLabel = 'will affect goldens'; - - group('PushGoldStatusToGithub', () { - late FakeConfig config; - late FakeClientContext clientContext; - FakeAuthenticatedContext authContext; - late FakeAuthenticationProvider auth; - late FakeDatastoreDB db; - late ApiRequestHandlerTester tester; - late PushGoldStatusToGithub handler; - FakeGraphQLClient githubGraphQLClient; - List checkRuns = []; - List engineCheckRuns = []; - late MockClient mockHttpClient; - late RepositorySlug slug; - late RepositorySlug engineSlug; - late RetryOptions retryOptions; - - final List records = []; - - setUp(() { - clientContext = FakeClientContext(); - authContext = FakeAuthenticatedContext(clientContext: clientContext); - auth = FakeAuthenticationProvider(clientContext: clientContext); - githubGraphQLClient = FakeGraphQLClient(); - db = FakeDatastoreDB(); - config = FakeConfig( - dbValue: db, - ); - tester = ApiRequestHandlerTester(context: authContext); - mockHttpClient = MockClient((_) async => http.Response('{}', HttpStatus.ok)); - retryOptions = const RetryOptions( - delayFactor: Duration(microseconds: 1), - maxDelay: Duration(microseconds: 2), - maxAttempts: 2, - ); - - githubGraphQLClient.mutateResultForOptions = (MutationOptions options) => createFakeQueryResult(); - githubGraphQLClient.queryResultForOptions = (QueryOptions options) { - if (options.variables['sRepoName'] == slug.name) { - return createGithubQueryResult(checkRuns); - } - if (options.variables['sRepoName'] == engineSlug.name) { - return createGithubQueryResult(engineCheckRuns); - } - return createGithubQueryResult([]); - }; - config.githubGraphQLClient = githubGraphQLClient; - config.flutterGoldPendingValue = 'pending'; - config.flutterGoldChangesValue = 'changes'; - config.flutterGoldSuccessValue = 'success'; - config.flutterGoldAlertConstantValue = 'flutter gold alert'; - config.flutterGoldInitialAlertValue = 'initial'; - config.flutterGoldFollowUpAlertValue = 'follow-up'; - config.flutterGoldDraftChangeValue = 'draft'; - config.flutterGoldStalePRValue = 'stale'; - - handler = PushGoldStatusToGithub( - config: config, - authenticationProvider: auth, - datastoreProvider: (DatastoreDB db) { - return DatastoreService( - db, - 5, - retryOptions: retryOptions, - ); - }, - goldClient: mockHttpClient, - ingestionDelay: Duration.zero, - ); - - slug = RepositorySlug('flutter', 'flutter'); - engineSlug = RepositorySlug('flutter', 'engine'); - checkRuns.clear(); - engineCheckRuns.clear(); - records.clear(); - log.onRecord.listen((LogRecord record) => records.add(record)); - }); - - group('in development environment', () { - setUp(() { - clientContext.isDevelopmentEnvironment = true; - }); - - test('Does nothing', () async { - config.githubClient = ThrowingGitHub(); - db.onCommit = - (List> insert, List> deletes) => throw AssertionError(); - db.addOnQuery((Iterable results) { - throw AssertionError(); - }); - final Body body = await tester.get(handler); - expect(body, same(Body.empty)); - }); - }); - - group('in non-development environment', () { - MockGitHub github; - MockPullRequestsService pullRequestsService; - late MockIssuesService issuesService; - late MockRepositoriesService repositoriesService; - List prsFromGitHub = []; - List enginePrsFromGitHub = []; - - setUp(() { - github = MockGitHub(); - pullRequestsService = MockPullRequestsService(); - issuesService = MockIssuesService(); - repositoriesService = MockRepositoriesService(); - when(github.pullRequests).thenReturn(pullRequestsService); - when(github.issues).thenReturn(issuesService); - when(github.repositories).thenReturn(repositoriesService); - - prsFromGitHub.clear(); - when(pullRequestsService.list(slug)).thenAnswer((Invocation _) { - return Stream.fromIterable(prsFromGitHub); - }); - - enginePrsFromGitHub.clear(); - when(pullRequestsService.list(engineSlug)).thenAnswer((Invocation _) { - return Stream.fromIterable(enginePrsFromGitHub); - }); - - when(repositoriesService.createStatus(any, any, any)).thenAnswer((_) async => RepositoryStatus()); - when(issuesService.createComment(any, any, any)).thenAnswer((_) async => IssueComment()); - when(issuesService.addLabelsToIssue(any, any, any)).thenAnswer((_) async => []); - config.githubClient = github; - clientContext.isDevelopmentEnvironment = false; - }); - - GithubGoldStatusUpdate newStatusUpdate( - RepositorySlug slug, - PullRequest pr, - String statusUpdate, - String sha, - String description, - ) { - return GithubGoldStatusUpdate( - key: db.emptyKey.append(GithubGoldStatusUpdate, id: pr.number), - status: statusUpdate, - pr: pr.number!, - head: sha, - updates: 0, - description: description, - repository: slug.fullName, - ); - } - - PullRequest newPullRequest(int number, String sha, String baseRef, {bool draft = false, DateTime? updated}) { - return PullRequest() - ..number = number - ..head = (PullRequestHead()..sha = sha) - ..base = (PullRequestHead()..ref = baseRef) - ..draft = draft - ..updatedAt = updated ?? DateTime.now(); - } - - group('does not update GitHub or Datastore', () { - setUp(() { - db.onCommit = - (List> insert, List> deletes) => throw AssertionError(); - when(repositoriesService.createStatus(any, any, any)).thenThrow(AssertionError()); - }); - - test('if there are no PRs', () async { - prsFromGitHub = []; - final Body body = await tester.get(handler); - expect(body, same(Body.empty)); - expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty); - expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty); - }); - - test('if there are no framework or web engine tests for this PR', () async { - checkRuns = [ - {'name': 'tool-test1', 'status': 'completed', 'conclusion': 'success'}, - ]; - final PullRequest flutterPr = newPullRequest(123, 'abc', 'master'); - prsFromGitHub = [flutterPr]; - final GithubGoldStatusUpdate status = newStatusUpdate(slug, flutterPr, '', '', ''); - db.values[status.key] = status; - - engineCheckRuns = [ - {'name': 'linux-host1', 'status': 'completed', 'conclusion': 'success'}, - ]; - final PullRequest enginePr = newPullRequest(456, 'def', 'main'); - enginePrsFromGitHub = [enginePr]; - final GithubGoldStatusUpdate engineStatus = newStatusUpdate(engineSlug, enginePr, '', '', ''); - db.values[engineStatus.key] = engineStatus; - - final Body body = await tester.get(handler); - expect(body, same(Body.empty)); - expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty); - expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty); - - // Should not apply labels or make comments - verifyNever( - issuesService.addLabelsToIssue( - slug, - flutterPr.number!, - [ - kGoldenFileLabel, - ], - ), - ); - verifyNever( - issuesService.addLabelsToIssue( - engineSlug, - enginePr.number!, - [ - kGoldenFileLabel, - ], - ), - ); - - verifyNever( - issuesService.createComment( - slug, - flutterPr.number!, - argThat(contains(config.flutterGoldCommentID(flutterPr))), - ), - ); - verifyNever( - issuesService.createComment( - engineSlug, - enginePr.number!, - argThat(contains(config.flutterGoldCommentID(enginePr))), - ), - ); - }); - - test('if there are no framework tests for this PR, exclude web builds', () async { - checkRuns = [ - {'name': 'web-test1', 'status': 'completed', 'conclusion': 'success'}, - ]; - final PullRequest pr = newPullRequest(123, 'abc', 'master'); - prsFromGitHub = [pr]; - final GithubGoldStatusUpdate status = newStatusUpdate(slug, pr, '', '', ''); - db.values[status.key] = status; - final Body body = await tester.get(handler); - expect(body, same(Body.empty)); - expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty); - expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty); - - // Should not apply labels or make comments - verifyNever( - issuesService.addLabelsToIssue( - slug, - pr.number!, - [ - kGoldenFileLabel, - ], - ), - ); - - verifyNever( - issuesService.createComment( - slug, - pr.number!, - argThat(contains(config.flutterGoldCommentID(pr))), - ), - ); - }); - - test('same commit, checks running, last status running', () async { - // Same commit - final PullRequest pr = newPullRequest(123, 'abc', 'master'); - prsFromGitHub = [pr]; - final GithubGoldStatusUpdate status = newStatusUpdate( - slug, - pr, - GithubGoldStatusUpdate.statusRunning, - 'abc', - config.flutterGoldPendingValue!, - ); - db.values[status.key] = status; - - // Checks still running - checkRuns = [ - {'name': 'framework', 'status': 'in_progress', 'conclusion': 'neutral'}, - {'name': 'web engine', 'status': 'in_progress', 'conclusion': 'neutral'}, - ]; - - final Body body = await tester.get(handler); - expect(body, same(Body.empty)); - expect(status.updates, 0); - expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty); - expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty); - - // Should not apply labels or make comments - verifyNever( - issuesService.addLabelsToIssue( - slug, - pr.number!, - [ - kGoldenFileLabel, - ], - ), - ); - - verifyNever( - issuesService.createComment( - slug, - pr.number!, - argThat(contains(config.flutterGoldCommentID(pr))), - ), - ); - }); - - test('same commit, checks complete, last status complete', () async { - // Same commit - final PullRequest pr = newPullRequest(123, 'abc', 'master'); - prsFromGitHub = [pr]; - final GithubGoldStatusUpdate status = newStatusUpdate( - slug, - pr, - GithubGoldStatusUpdate.statusCompleted, - 'abc', - config.flutterGoldSuccessValue!, - ); - db.values[status.key] = status; - - // Checks complete - checkRuns = [ - {'name': 'framework', 'status': 'completed', 'conclusion': 'success'}, - {'name': 'web engine', 'status': 'completed', 'conclusion': 'success'}, - ]; - - final Body body = await tester.get(handler); - expect(body, same(Body.empty)); - expect(status.updates, 0); - expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty); - expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty); - - // Should not apply labels or make comments - verifyNever( - issuesService.addLabelsToIssue( - slug, - pr.number!, - [ - kGoldenFileLabel, - ], - ), - ); - - verifyNever( - issuesService.createComment( - slug, - pr.number!, - argThat(contains(config.flutterGoldCommentID(pr))), - ), - ); - }); - - test('same commit, checks complete, last status & gold status is running/awaiting triage, should not comment', - () async { - // Same commit - final PullRequest pr = newPullRequest(123, 'abc', 'master'); - prsFromGitHub = [pr]; - final GithubGoldStatusUpdate status = newStatusUpdate( - slug, - pr, - GithubGoldStatusUpdate.statusRunning, - 'abc', - config.flutterGoldChangesValue!, - ); - db.values[status.key] = status; - - final PullRequest enginePr = newPullRequest(456, 'def', 'main'); - enginePrsFromGitHub = [enginePr]; - final GithubGoldStatusUpdate engineStatus = newStatusUpdate( - engineSlug, - enginePr, - GithubGoldStatusUpdate.statusRunning, - 'def', - config.flutterGoldChangesValue!, - ); - db.values[engineStatus.key] = engineStatus; - - // Checks complete - checkRuns = [ - {'name': 'framework', 'status': 'completed', 'conclusion': 'success'}, - ]; - engineCheckRuns = [ - {'name': 'web engine', 'status': 'completed', 'conclusion': 'success'}, - ]; - - // Gold status is running - mockHttpClient = MockClient((http.Request request) async { - if (request.url.toString() == - 'https://flutter-gold.skia.org/json/v1/changelist_summary/github/${pr.number}') { - return http.Response(tryjobDigests(pr), HttpStatus.ok); - } - if (request.url.toString() == - 'https://flutter-engine-gold.skia.org/json/v1/changelist_summary/github/${enginePr.number}') { - return http.Response(tryjobDigests(enginePr), HttpStatus.ok); - } - throw const HttpException('Unexpected http request'); - }); - handler = PushGoldStatusToGithub( - config: config, - authenticationProvider: auth, - datastoreProvider: (DatastoreDB db) { - return DatastoreService( - config.db, - 5, - retryOptions: retryOptions, - ); - }, - goldClient: mockHttpClient, - ingestionDelay: Duration.zero, - ); - - // Already commented for this commit. - when(issuesService.listCommentsByIssue(slug, pr.number!)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = config.flutterGoldCommentID(pr), - ), - ); - when(issuesService.listCommentsByIssue(engineSlug, enginePr.number!)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = config.flutterGoldCommentID(enginePr), - ), - ); - - final Body body = await tester.get(handler); - expect(body, same(Body.empty)); - expect(status.updates, 0); - expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty); - expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty); - - // Should not apply labels or make comments - verifyNever( - issuesService.addLabelsToIssue( - slug, - pr.number!, - [ - kGoldenFileLabel, - ], - ), - ); - verifyNever( - issuesService.addLabelsToIssue( - engineSlug, - enginePr.number!, - [ - kGoldenFileLabel, - ], - ), - ); - - verifyNever( - issuesService.createComment( - slug, - pr.number!, - argThat(contains(config.flutterGoldCommentID(pr))), - ), - ); - verifyNever( - issuesService.createComment( - engineSlug, - enginePr.number!, - argThat(contains(config.flutterGoldCommentID(enginePr))), - ), - ); - }); - - test('does nothing for branches not staged to land on main/master', () async { - // New commit - final PullRequest pr = newPullRequest(123, 'abc', 'release'); - prsFromGitHub = [pr]; - final GithubGoldStatusUpdate status = newStatusUpdate(slug, pr, '', '', ''); - db.values[status.key] = status; - - // All checks completed - checkRuns = [ - {'name': 'framework', 'status': 'completed', 'conclusion': 'success'}, - ]; - - final Body body = await tester.get(handler); - expect(body, same(Body.empty)); - expect(status.updates, 0); - expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty); - expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty); - - // Should not apply labels or make comments - verifyNever( - issuesService.addLabelsToIssue( - slug, - pr.number!, - [ - kGoldenFileLabel, - ], - ), - ); - - verifyNever( - issuesService.createComment( - slug, - pr.number!, - argThat(contains(config.flutterGoldCommentID(pr))), - ), - ); - }); - - test('does not post for draft PRs, does not query Gold', () async { - // New commit, draft PR - final PullRequest pr = newPullRequest(123, 'abc', 'master', draft: true); - prsFromGitHub = [pr]; - final GithubGoldStatusUpdate status = newStatusUpdate(slug, pr, '', '', ''); - db.values[status.key] = status; - - // Checks completed - checkRuns = [ - {'name': 'framework', 'status': 'completed', 'conclusion': 'success'}, - ]; - - final Body body = await tester.get(handler); - expect(body, same(Body.empty)); - expect(status.updates, 0); - expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty); - expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty); - - // Should not apply labels or make comments - verifyNever( - issuesService.addLabelsToIssue( - slug, - pr.number!, - [ - kGoldenFileLabel, - ], - ), - ); - - verifyNever( - issuesService.createComment( - slug, - pr.number!, - any, - ), - ); - }); - - test('does not post for draft PRs, does not query Gold', () async { - // New commit, draft PR - final PullRequest pr = newPullRequest(123, 'abc', 'master', draft: true); - prsFromGitHub = [pr]; - final GithubGoldStatusUpdate status = newStatusUpdate(slug, pr, '', '', ''); - db.values[status.key] = status; - - // Checks completed - checkRuns = [ - {'name': 'Linux', 'status': 'completed', 'conclusion': 'success'}, - ]; - - final Body body = await tester.get(handler); - expect(body, same(Body.empty)); - expect(status.updates, 0); - expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty); - expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty); - - // Should not apply labels or make comments - verifyNever( - issuesService.addLabelsToIssue( - slug, - pr.number!, - [ - kGoldenFileLabel, - ], - ), - ); - - verifyNever( - issuesService.createComment( - slug, - pr.number!, - any, - ), - ); - }); - - test('does not post for stale PRs, does not query Gold, stale comment', () async { - // New commit, draft PR - final PullRequest pr = - newPullRequest(123, 'abc', 'master', updated: DateTime.now().subtract(const Duration(days: 30))); - prsFromGitHub = [pr]; - final GithubGoldStatusUpdate status = newStatusUpdate(slug, pr, '', '', ''); - db.values[status.key] = status; - - // Checks completed - checkRuns = [ - {'name': 'framework', 'status': 'completed', 'conclusion': 'success'}, - ]; - - // Have not already commented for this commit. - when(issuesService.listCommentsByIssue(slug, pr.number!)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = 'some other comment', - ), - ); - - final Body body = await tester.get(handler); - expect(body, same(Body.empty)); - expect(status.updates, 0); - expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty); - expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty); - - // Should not apply labels, should comment to update - verifyNever( - issuesService.addLabelsToIssue( - slug, - pr.number!, - [ - kGoldenFileLabel, - ], - ), - ); - - verify( - issuesService.createComment( - slug, - pr.number!, - argThat(contains(config.flutterGoldStalePRValue)), - ), - ).called(1); - }); - - test('will only comment once on stale PRs', () async { - // New commit, draft PR - final PullRequest pr = - newPullRequest(123, 'abc', 'master', updated: DateTime.now().subtract(const Duration(days: 30))); - prsFromGitHub = [pr]; - final GithubGoldStatusUpdate status = newStatusUpdate(slug, pr, '', '', ''); - db.values[status.key] = status; - - // Checks completed - checkRuns = [ - {'name': 'framework', 'status': 'completed', 'conclusion': 'success'}, - ]; - - // Already commented to update. - when(issuesService.listCommentsByIssue(slug, pr.number!)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = config.flutterGoldStalePRValue, - ), - ); - - final Body body = await tester.get(handler); - expect(body, same(Body.empty)); - expect(status.updates, 0); - expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty); - expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty); - - // Should not apply labels or make comments - verifyNever( - issuesService.addLabelsToIssue( - slug, - pr.number!, - [ - kGoldenFileLabel, - ], - ), - ); - - verifyNever( - issuesService.createComment( - slug, - pr.number!, - any, - ), - ); - }); - - test('will not fire off stale warning for non-framework PRs', () async { - // New commit, draft PR - final PullRequest pr = - newPullRequest(123, 'abc', 'master', updated: DateTime.now().subtract(const Duration(days: 30))); - prsFromGitHub = [pr]; - final GithubGoldStatusUpdate status = newStatusUpdate(slug, pr, '', '', ''); - db.values[status.key] = status; - - // Checks completed - checkRuns = [ - {'name': 'tool-test-1', 'status': 'completed', 'conclusion': 'success'}, - ]; - - // Already commented to update. - when(issuesService.listCommentsByIssue(slug, pr.number!)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = 'some other comment', - ), - ); - - final Body body = await tester.get(handler); - expect(body, same(Body.empty)); - expect(status.updates, 0); - expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty); - expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty); - - // Should not apply labels or make comments - verifyNever( - issuesService.addLabelsToIssue( - slug, - pr.number!, - [ - kGoldenFileLabel, - ], - ), - ); - - verifyNever( - issuesService.createComment( - slug, - pr.number!, - any, - ), - ); - }); - }); - - group('updates GitHub and/or Datastore', () { - test('new commit, checks running', () async { - // New commit - final PullRequest flutterPr = newPullRequest(123, 'f-abc', 'master'); - prsFromGitHub = [flutterPr]; - final GithubGoldStatusUpdate status = newStatusUpdate(slug, flutterPr, '', '', ''); - - final PullRequest enginePr = newPullRequest(567, 'e-abc', 'main'); - enginePrsFromGitHub = [enginePr]; - final GithubGoldStatusUpdate engineStatus = newStatusUpdate(engineSlug, enginePr, '', '', ''); - - db.values[status.key] = status; - db.values[engineStatus.key] = engineStatus; - - // Checks running - checkRuns = [ - {'name': 'framework', 'status': 'in_progress', 'conclusion': 'neutral'}, - ]; - engineCheckRuns = [ - {'name': 'web engine', 'status': 'in_progress', 'conclusion': 'neutral'}, - ]; - - final Body body = await tester.get(handler); - expect(body, same(Body.empty)); - expect(status.updates, 1); - expect(status.status, GithubGoldStatusUpdate.statusRunning); - expect(engineStatus.updates, 1); - expect(engineStatus.status, GithubGoldStatusUpdate.statusRunning); - expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty); - expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty); - - // Should not apply labels or make comments - verifyNever( - issuesService.addLabelsToIssue( - slug, - flutterPr.number!, - [ - kGoldenFileLabel, - ], - ), - ); - verifyNever( - issuesService.addLabelsToIssue( - engineSlug, - enginePr.number!, - [ - kGoldenFileLabel, - ], - ), - ); - - verifyNever( - issuesService.createComment( - slug, - flutterPr.number!, - argThat(contains(config.flutterGoldCommentID(flutterPr))), - ), - ); - verifyNever( - issuesService.createComment( - engineSlug, - enginePr.number!, - argThat(contains(config.flutterGoldCommentID(enginePr))), - ), - ); - }); - - test('includes misc test shards', () async { - // New commit - final PullRequest pr = newPullRequest(123, 'abc', 'master'); - prsFromGitHub = [pr]; - final GithubGoldStatusUpdate status = newStatusUpdate(slug, pr, '', '', ''); - db.values[status.key] = status; - - // Checks completed - checkRuns = [ - {'name': 'misc', 'status': 'completed', 'conclusion': 'success'}, - ]; - - // Change detected by Gold - mockHttpClient = MockClient((http.Request request) async { - if (request.url.toString() == - 'https://flutter-gold.skia.org/json/v1/changelist_summary/github/${pr.number}') { - return http.Response(tryjobEmpty(), HttpStatus.ok); - } - throw const HttpException('Unexpected http request'); - }); - handler = PushGoldStatusToGithub( - config: config, - authenticationProvider: auth, - datastoreProvider: (DatastoreDB db) { - return DatastoreService( - config.db, - 5, - retryOptions: retryOptions, - ); - }, - goldClient: mockHttpClient, - ingestionDelay: Duration.zero, - ); - - final Body body = await tester.get(handler); - expect(body, same(Body.empty)); - expect(status.updates, 1); - expect(status.status, GithubGoldStatusUpdate.statusCompleted); - expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty); - expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty); - - // Should not label or comment - verifyNever( - issuesService.addLabelsToIssue( - slug, - pr.number!, - [ - kGoldenFileLabel, - ], - ), - ); - - verifyNever( - issuesService.createComment( - slug, - pr.number!, - argThat(contains(config.flutterGoldCommentID(pr))), - ), - ); - }); - - test('new commit, checks complete, no changes detected', () async { - // New commit - final PullRequest pr = newPullRequest(123, 'abc', 'master'); - prsFromGitHub = [pr]; - final GithubGoldStatusUpdate status = newStatusUpdate(slug, pr, '', '', ''); - db.values[status.key] = status; - - // Checks completed - checkRuns = [ - {'name': 'framework', 'status': 'completed', 'conclusion': 'success'}, - ]; - - // Change detected by Gold - mockHttpClient = MockClient((http.Request request) async { - if (request.url.toString() == - 'https://flutter-gold.skia.org/json/v1/changelist_summary/github/${pr.number}') { - return http.Response(tryjobEmpty(), HttpStatus.ok); - } - throw const HttpException('Unexpected http request'); - }); - handler = PushGoldStatusToGithub( - config: config, - authenticationProvider: auth, - datastoreProvider: (DatastoreDB db) { - return DatastoreService( - config.db, - 5, - retryOptions: retryOptions, - ); - }, - goldClient: mockHttpClient, - ingestionDelay: Duration.zero, - ); - - final Body body = await tester.get(handler); - expect(body, same(Body.empty)); - expect(status.updates, 1); - expect(status.status, GithubGoldStatusUpdate.statusCompleted); - expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty); - expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty); - - // Should not label or comment - verifyNever( - issuesService.addLabelsToIssue( - slug, - pr.number!, - [ - kGoldenFileLabel, - ], - ), - ); - - verifyNever( - issuesService.createComment( - slug, - pr.number!, - argThat(contains(config.flutterGoldCommentID(pr))), - ), - ); - }); - - test('new commit, checks complete, change detected, should comment', () async { - // New commit - final PullRequest pr = newPullRequest(123, 'abc', 'master'); - prsFromGitHub = [pr]; - final GithubGoldStatusUpdate status = newStatusUpdate(slug, pr, '', '', ''); - db.values[status.key] = status; - - // Checks completed - checkRuns = [ - {'name': 'framework', 'status': 'completed', 'conclusion': 'success'}, - ]; - - // Change detected by Gold - mockHttpClient = MockClient((http.Request request) async { - if (request.url.toString() == - 'https://flutter-gold.skia.org/json/v1/changelist_summary/github/${pr.number}') { - return http.Response(tryjobDigests(pr), HttpStatus.ok); - } - throw const HttpException('Unexpected http request'); - }); - handler = PushGoldStatusToGithub( - config: config, - authenticationProvider: auth, - datastoreProvider: (DatastoreDB db) { - return DatastoreService( - config.db, - 5, - retryOptions: retryOptions, - ); - }, - goldClient: mockHttpClient, - ingestionDelay: Duration.zero, - ); - - // Have not already commented for this commit. - when(issuesService.listCommentsByIssue(slug, pr.number!)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = 'some other comment', - ), - ); - - final Body body = await tester.get(handler); - expect(body, same(Body.empty)); - expect(status.updates, 1); - expect(status.status, GithubGoldStatusUpdate.statusRunning); - expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty); - expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty); - - // Should label and comment - verify( - issuesService.addLabelsToIssue( - slug, - pr.number!, - [ - kGoldenFileLabel, - ], - ), - ).called(1); - - verify( - issuesService.createComment( - slug, - pr.number!, - argThat(contains(config.flutterGoldCommentID(pr))), - ), - ).called(1); - }); - - test('same commit, checks complete, last status was waiting & gold status is needing triage, should comment', - () async { - // Same commit - final PullRequest pr = newPullRequest(123, 'abc', 'master'); - prsFromGitHub = [pr]; - final GithubGoldStatusUpdate status = - newStatusUpdate(slug, pr, GithubGoldStatusUpdate.statusRunning, 'abc', config.flutterGoldPendingValue!); - db.values[status.key] = status; - - // Checks complete - checkRuns = [ - {'name': 'framework', 'status': 'completed', 'conclusion': 'success'}, - ]; - - // Gold status is running - mockHttpClient = MockClient((http.Request request) async { - if (request.url.toString() == - 'https://flutter-gold.skia.org/json/v1/changelist_summary/github/${pr.number}') { - return http.Response(tryjobDigests(pr), HttpStatus.ok); - } - throw const HttpException('Unexpected http request'); - }); - handler = PushGoldStatusToGithub( - config: config, - authenticationProvider: auth, - datastoreProvider: (DatastoreDB db) { - return DatastoreService( - config.db, - 5, - retryOptions: retryOptions, - ); - }, - goldClient: mockHttpClient, - ingestionDelay: Duration.zero, - ); - - // Have not already commented for this commit. - when(issuesService.listCommentsByIssue(slug, pr.number!)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = 'some other comment', - ), - ); - - final Body body = await tester.get(handler); - expect(body, same(Body.empty)); - expect(status.updates, 1); - expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty); - expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty); - - // Should apply labels and make comment - verify( - issuesService.addLabelsToIssue( - slug, - pr.number!, - [ - kGoldenFileLabel, - ], - ), - ).called(1); - - verify( - issuesService.createComment( - slug, - pr.number!, - argThat(contains(config.flutterGoldCommentID(pr))), - ), - ).called(1); - }); - - test('uses shorter comment after first comment to reduce noise', () async { - // Same commit - final PullRequest pr = newPullRequest(123, 'abc', 'master'); - prsFromGitHub = [pr]; - final GithubGoldStatusUpdate status = - newStatusUpdate(slug, pr, GithubGoldStatusUpdate.statusRunning, 'abc', config.flutterGoldPendingValue!); - db.values[status.key] = status; - - // Checks complete - checkRuns = [ - {'name': 'framework', 'completed': 'in_progress', 'conclusion': 'success'}, - ]; - - // Gold status is running - mockHttpClient = MockClient((http.Request request) async { - if (request.url.toString() == - 'https://flutter-gold.skia.org/json/v1/changelist_summary/github/${pr.number}') { - return http.Response(tryjobDigests(pr), HttpStatus.ok); - } - throw const HttpException('Unexpected http request'); - }); - handler = PushGoldStatusToGithub( - config: config, - authenticationProvider: auth, - datastoreProvider: (DatastoreDB db) { - return DatastoreService( - config.db, - 5, - retryOptions: retryOptions, - ); - }, - goldClient: mockHttpClient, - ingestionDelay: Duration.zero, - ); - - // Have not already commented for this commit. - when(issuesService.listCommentsByIssue(slug, pr.number!)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = config.flutterGoldInitialAlertValue, - ), - ); - - final Body body = await tester.get(handler); - expect(body, same(Body.empty)); - expect(status.updates, 1); - expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty); - expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty); - - // Should apply labels and make comment - verify( - issuesService.addLabelsToIssue( - slug, - pr.number!, - [ - kGoldenFileLabel, - ], - ), - ).called(1); - - verify( - issuesService.createComment( - slug, - pr.number!, - argThat(contains(config.flutterGoldFollowUpAlertValue)), - ), - ).called(1); - verifyNever( - issuesService.createComment( - slug, - pr.number!, - argThat(contains(config.flutterGoldInitialAlertValue)), - ), - ); - verifyNever( - issuesService.createComment( - slug, - pr.number!, - argThat(contains(config.flutterGoldAlertConstantValue)), - ), - ); - }); - - test('same commit, checks complete, new status, should not comment', () async { - // Same commit: abc - final PullRequest pr = newPullRequest(123, 'abc', 'master'); - prsFromGitHub = [pr]; - final GithubGoldStatusUpdate status = - newStatusUpdate(slug, pr, GithubGoldStatusUpdate.statusRunning, 'abc', config.flutterGoldPendingValue!); - db.values[status.key] = status; - - // Checks completed - checkRuns = [ - {'name': 'framework', 'status': 'completed', 'conclusion': 'success'}, - ]; - - // New status: completed/triaged/no changes - mockHttpClient = MockClient((http.Request request) async { - if (request.url.toString() == - 'https://flutter-gold.skia.org/json/v1/changelist_summary/github/${pr.number}') { - return http.Response(tryjobEmpty(), HttpStatus.ok); - } - throw const HttpException('Unexpected http request'); - }); - handler = PushGoldStatusToGithub( - config: config, - authenticationProvider: auth, - datastoreProvider: (DatastoreDB db) { - return DatastoreService( - config.db, - 5, - retryOptions: retryOptions, - ); - }, - goldClient: mockHttpClient, - ingestionDelay: Duration.zero, - ); - - when(issuesService.listCommentsByIssue(slug, pr.number!)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = 'some other comment', - ), - ); - - final Body body = await tester.get(handler); - expect(body, same(Body.empty)); - expect(status.updates, 1); - expect(status.status, GithubGoldStatusUpdate.statusCompleted); - expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty); - expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty); - - // Should not label or comment - verifyNever( - issuesService.addLabelsToIssue( - slug, - pr.number!, - [ - kGoldenFileLabel, - ], - ), - ); - - verifyNever( - issuesService.createComment( - slug, - pr.number!, - argThat(contains(config.flutterGoldCommentID(pr))), - ), - ); - }); - - test('will inform contributor of unresolved check for ATF draft status', () async { - // New commit, draft PR - final PullRequest pr = newPullRequest(123, 'abc', 'master', draft: true); - prsFromGitHub = [pr]; - final GithubGoldStatusUpdate status = - newStatusUpdate(slug, pr, GithubGoldStatusUpdate.statusRunning, 'abc', config.flutterGoldPendingValue!); - db.values[status.key] = status; - - // Checks completed - checkRuns = [ - {'name': 'framework', 'status': 'completed', 'conclusion': 'success'}, - ]; - when(issuesService.listCommentsByIssue(slug, pr.number!)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = 'some other comment', - ), - ); - - final Body body = await tester.get(handler); - expect(body, same(Body.empty)); - expect(status.updates, 0); - expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty); - expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty); - - // Should not apply labels or make comments - verifyNever( - issuesService.addLabelsToIssue( - slug, - pr.number!, - [ - kGoldenFileLabel, - ], - ), - ); - - verify( - issuesService.createComment( - slug, - pr.number!, - argThat(contains(config.flutterGoldDraftChangeValue)), - ), - ).called(1); - }); - - test('will only inform contributor of unresolved check for ATF draft status once', () async { - // New commit, draft PR - final PullRequest pr = newPullRequest(123, 'abc', 'master', draft: true); - prsFromGitHub = [pr]; - final GithubGoldStatusUpdate status = - newStatusUpdate(slug, pr, GithubGoldStatusUpdate.statusRunning, 'abc', config.flutterGoldPendingValue!); - db.values[status.key] = status; - - // Checks completed - checkRuns = [ - {'name': 'framework', 'status': 'completed', 'conclusion': 'success'}, - ]; - when(issuesService.listCommentsByIssue(slug, pr.number!)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = config.flutterGoldDraftChangeValue, - ), - ); - - final Body body = await tester.get(handler); - expect(body, same(Body.empty)); - expect(status.updates, 0); - expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty); - expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty); - - // Should not apply labels or make comments - verifyNever( - issuesService.addLabelsToIssue( - slug, - pr.number!, - [ - kGoldenFileLabel, - ], - ), - ); - - verifyNever( - issuesService.createComment( - slug, - pr.number!, - argThat(contains(config.flutterGoldDraftChangeValue)), - ), - ); - }); - - test('delivers pending state for failing checks, does not query Gold', () async { - // New commit - final PullRequest pr = newPullRequest(123, 'abc', 'master'); - prsFromGitHub = [pr]; - final GithubGoldStatusUpdate status = newStatusUpdate(slug, pr, '', '', ''); - db.values[status.key] = status; - - // Checks failed - checkRuns = [ - {'name': 'framework', 'status': 'completed', 'conclusion': 'failure'}, - ]; - - final Body body = await tester.get(handler); - expect(body, same(Body.empty)); - expect(status.updates, 1); - expect(status.status, GithubGoldStatusUpdate.statusRunning); - expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty); - expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty); - - // Should not apply labels or make comments - verifyNever( - issuesService.addLabelsToIssue( - slug, - pr.number!, - [ - kGoldenFileLabel, - ], - ), - ); - - verifyNever( - issuesService.createComment( - slug, - pr.number!, - argThat(contains(config.flutterGoldCommentID(pr))), - ), - ); - }); - }); - - test('Completed pull request does not skip follow-up prs with early return', () async { - final PullRequest completedPR = newPullRequest(123, 'abc', 'master'); - final PullRequest followUpPR = newPullRequest(456, 'def', 'master'); - prsFromGitHub = [ - completedPR, - followUpPR, - ]; - final GithubGoldStatusUpdate completedStatus = newStatusUpdate( - slug, - completedPR, - GithubGoldStatusUpdate.statusCompleted, - 'abc', - config.flutterGoldSuccessValue!, - ); - final GithubGoldStatusUpdate followUpStatus = newStatusUpdate(slug, followUpPR, '', '', ''); - db.values[completedStatus.key] = completedStatus; - db.values[followUpStatus.key] = followUpStatus; - - // Checks completed - checkRuns = [ - {'name': 'framework', 'status': 'completed', 'conclusion': 'success'}, - ]; - - // New status: completed/triaged/no changes - mockHttpClient = MockClient((http.Request request) async { - if (request.url.toString() == - 'https://flutter-gold.skia.org/json/v1/changelist_summary/github/${completedPR.number}') { - return http.Response(tryjobEmpty(), HttpStatus.ok); - } - if (request.url.toString() == - 'https://flutter-gold.skia.org/json/v1/changelist_summary/github/${followUpPR.number}') { - return http.Response(tryjobEmpty(), HttpStatus.ok); - } - throw const HttpException('Unexpected http request'); - }); - handler = PushGoldStatusToGithub( - config: config, - authenticationProvider: auth, - datastoreProvider: (DatastoreDB db) { - return DatastoreService( - config.db, - 5, - retryOptions: retryOptions, - ); - }, - goldClient: mockHttpClient, - ingestionDelay: Duration.zero, - ); - - when(issuesService.listCommentsByIssue(slug, completedPR.number!)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = 'some other comment', - ), - ); - - final Body body = await tester.get(handler); - expect(body, same(Body.empty)); - expect(completedStatus.updates, 0); - expect(followUpStatus.updates, 1); - expect(completedStatus.status, GithubGoldStatusUpdate.statusCompleted); - expect(followUpStatus.status, GithubGoldStatusUpdate.statusCompleted); - expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty); - expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty); - }); - - test('accounts for null status description when parsing for Luci builds', () async { - // Same commit - final PullRequest pr = newPullRequest(123, 'abc', 'master'); - prsFromGitHub = [pr]; - final GithubGoldStatusUpdate status = newStatusUpdate( - slug, - pr, - GithubGoldStatusUpdate.statusRunning, - 'abc', - config.flutterGoldPendingValue!, - ); - db.values[status.key] = status; - - // null status for luci build - checkRuns = [ - { - 'name': 'framework', - 'status': null, - 'conclusion': null, - } - ]; - - final Body body = await tester.get(handler); - expect(body, same(Body.empty)); - expect(status.updates, 0); - expect(records.where((LogRecord record) => record.level == Level.WARNING), isEmpty); - expect(records.where((LogRecord record) => record.level == Level.SEVERE), isEmpty); - - // Should not apply labels or make comments - verifyNever( - issuesService.addLabelsToIssue( - slug, - pr.number!, - [ - kGoldenFileLabel, - ], - ), - ); - - verifyNever( - issuesService.createComment( - slug, - pr.number!, - argThat(contains(config.flutterGoldCommentID(pr))), - ), - ); - }); - - test('uses the correct Gold endpoint to get status', () async { - // New commit - final PullRequest pr = newPullRequest(123, 'abc', 'master'); - prsFromGitHub = [pr]; - final GithubGoldStatusUpdate status = newStatusUpdate(slug, pr, '', '', ''); - db.values[status.key] = status; - - final PullRequest enginePr = newPullRequest(456, 'def', 'main'); - enginePrsFromGitHub = [enginePr]; - final GithubGoldStatusUpdate engineStatus = newStatusUpdate(engineSlug, enginePr, '', '', ''); - db.values[engineStatus.key] = engineStatus; - - // Checks completed - checkRuns = [ - {'name': 'framework', 'status': 'completed', 'conclusion': 'success'}, - ]; - engineCheckRuns = [ - {'name': 'web engine', 'status': 'completed', 'conclusion': 'success'}, - ]; - - // Requests sent to Gold. - final List goldRequests = []; - mockHttpClient = MockClient((http.Request request) async { - final String requestUrl = request.url.toString(); - goldRequests.add(requestUrl); - - final int prNumber = int.parse(requestUrl.split('/').last); - final PullRequest requestedPr; - if (prNumber == pr.number) { - requestedPr = pr; - } else if (prNumber == enginePr.number) { - requestedPr = enginePr; - } else { - throw HttpException('Unexpected http request for PR#$prNumber'); - } - return http.Response(tryjobDigests(requestedPr), HttpStatus.ok); - }); - handler = PushGoldStatusToGithub( - config: config, - authenticationProvider: auth, - datastoreProvider: (DatastoreDB db) { - return DatastoreService( - config.db, - 5, - retryOptions: retryOptions, - ); - }, - goldClient: mockHttpClient, - ingestionDelay: Duration.zero, - ); - - // Have not already commented for this commit. - when(issuesService.listCommentsByIssue(slug, pr.number!)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = 'some other comment', - ), - ); - when(issuesService.listCommentsByIssue(engineSlug, enginePr.number!)).thenAnswer( - (_) => Stream.value( - IssueComment()..body = 'some other comment', - ), - ); - - await tester.get(handler); - - expect(goldRequests, [ - 'https://flutter-gold.skia.org/json/v1/changelist_summary/github/${pr.number}', - 'https://flutter-engine-gold.skia.org/json/v1/changelist_summary/github/${enginePr.number}', - ]); - }); - }); - }); -} - -QueryResult createGithubQueryResult(List statuses) { - return createFakeQueryResult( - data: { - 'repository': { - 'pullRequest': { - 'commits': { - 'nodes': [ - { - 'commit': { - 'checkSuites': { - 'nodes': [ - { - 'checkRuns': {'nodes': statuses}, - } - ], - }, - }, - } - ], - }, - }, - }, - }, - ); -} - -/// JSON response template for Skia Gold empty tryjob status request. -String tryjobEmpty() { - return ''' - { - "changelist_id": "123", - "patchsets": [ - { - "new_images": 0, - "new_untriaged_images": 0, - "total_untriaged_images": 0, - "patchset_id": "abc", - "patchset_order": 1 - } - ], - "outdated": false - } - '''; -} - -/// JSON response template for Skia Gold untriaged tryjob status request. -String tryjobDigests(PullRequest pr) { - return ''' - { - "changelist_id": "${pr.number!}", - "patchsets": [ - { - "new_images": 1, - "new_untriaged_images": 1, - "total_untriaged_images": 1, - "patchset_id": "${pr.head!.sha!}", - "patchset_order": 1 - } - ], - "outdated": false - } - '''; -} diff --git a/app_dart/test/request_handlers/reset_prod_task_test.dart b/app_dart/test/request_handlers/reset_prod_task_test.dart deleted file mode 100644 index 6be487aa7..000000000 --- a/app_dart/test/request_handlers/reset_prod_task_test.dart +++ /dev/null @@ -1,188 +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:cocoon_service/cocoon_service.dart'; -import 'package:cocoon_service/src/model/appengine/commit.dart'; -import 'package:cocoon_service/src/model/appengine/task.dart'; -import 'package:cocoon_service/src/request_handling/exceptions.dart'; -import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; - -import '../src/datastore/fake_config.dart'; -import '../src/datastore/fake_datastore.dart'; -import '../src/request_handling/api_request_handler_tester.dart'; -import '../src/request_handling/fake_authentication.dart'; -import '../src/service/fake_scheduler.dart'; -import '../src/utilities/entity_generators.dart'; -import '../src/utilities/mocks.dart'; - -void main() { - group('ResetProdTask', () { - FakeClientContext clientContext; - late ResetProdTask handler; - late FakeConfig config; - FakeKeyHelper keyHelper; - late MockLuciBuildService mockLuciBuildService; - late ApiRequestHandlerTester tester; - late Commit commit; - late Task task; - - setUp(() { - final FakeDatastoreDB datastoreDB = FakeDatastoreDB(); - clientContext = FakeClientContext(); - clientContext.isDevelopmentEnvironment = false; - keyHelper = FakeKeyHelper(applicationContext: clientContext.applicationContext); - config = FakeConfig( - dbValue: datastoreDB, - keyHelperValue: keyHelper, - supportedBranchesValue: [ - Config.defaultBranch(Config.flutterSlug), - ], - ); - final FakeAuthenticatedContext authContext = FakeAuthenticatedContext(clientContext: clientContext); - tester = ApiRequestHandlerTester(context: authContext); - mockLuciBuildService = MockLuciBuildService(); - handler = ResetProdTask( - config: config, - authenticationProvider: FakeAuthenticationProvider(clientContext: clientContext), - luciBuildService: mockLuciBuildService, - scheduler: FakeScheduler( - config: config, - ciYaml: exampleConfig, - ), - ); - commit = generateCommit(1); - task = generateTask( - 1, - name: 'Linux A', - parent: commit, - status: Task.statusFailed, - ); - tester.requestData = { - 'Key': config.keyHelper.encode(task.key), - }; - - when( - mockLuciBuildService.checkRerunBuilder( - commit: anyNamed('commit'), - datastore: anyNamed('datastore'), - task: anyNamed('task'), - target: anyNamed('target'), - tags: anyNamed('tags'), - ignoreChecks: anyNamed('ignoreChecks'), - ), - ).thenAnswer((_) async => true); - }); - test('Schedule new task', () async { - config.db.values[task.key] = task; - config.db.values[commit.key] = commit; - expect(await tester.post(handler), Body.empty); - }); - - test('Re-schedule existing task', () async { - config.db.values[task.key] = task; - config.db.values[commit.key] = commit; - expect(await tester.post(handler), Body.empty); - }); - - test('Re-schedule passing all the parameters', () async { - config.db.values[task.key] = task; - config.db.values[commit.key] = commit; - tester.requestData = { - 'Commit': commit.sha, - 'Task': task.name, - 'Repo': commit.slug.name, - }; - expect(await tester.post(handler), Body.empty); - verify( - mockLuciBuildService.checkRerunBuilder( - commit: anyNamed('commit'), - datastore: anyNamed('datastore'), - task: anyNamed('task'), - target: anyNamed('target'), - tags: anyNamed('tags'), - ignoreChecks: true, - ), - ).called(1); - }); - - test('Rerun all failed tasks when task name is all', () async { - final Task taskA = generateTask(2, name: 'Linux A', parent: commit, status: Task.statusFailed); - final Task taskB = generateTask(3, name: 'Mac A', parent: commit, status: Task.statusFailed); - config.db.values[taskA.key] = taskA; - config.db.values[taskB.key] = taskB; - config.db.values[commit.key] = commit; - tester.requestData = { - 'Commit': commit.sha, - 'Task': 'all', - 'Repo': commit.slug.name, - }; - expect(await tester.post(handler), Body.empty); - verify( - mockLuciBuildService.checkRerunBuilder( - commit: anyNamed('commit'), - datastore: anyNamed('datastore'), - task: anyNamed('task'), - target: anyNamed('target'), - tags: anyNamed('tags'), - ignoreChecks: false, - ), - ).called(2); - }); - - test('Rerun all runs nothing when everything is passed', () async { - final Task task = generateTask(2, name: 'Windows A', parent: commit, status: Task.statusSucceeded); - config.db.values[task.key] = task; - config.db.values[commit.key] = commit; - tester.requestData = { - 'Commit': commit.sha, - 'Task': 'all', - 'Repo': commit.slug.name, - }; - expect(await tester.post(handler), Body.empty); - verifyNever( - mockLuciBuildService.checkRerunBuilder( - commit: anyNamed('commit'), - datastore: anyNamed('datastore'), - task: anyNamed('task'), - target: anyNamed('target'), - tags: anyNamed('tags'), - ignoreChecks: false, - ), - ); - }); - - test('Re-schedule without any parameters raises exception', () async { - tester.requestData = {}; - expect(() => tester.post(handler), throwsA(isA())); - }); - - test('Re-schedule existing task even though taskName is missing in the task', () async { - config.db.values[task.key] = task; - config.db.values[commit.key] = commit; - expect(await tester.post(handler), Body.empty); - }); - - test('Fails if task is not rerun', () async { - when( - mockLuciBuildService.checkRerunBuilder( - commit: anyNamed('commit'), - datastore: anyNamed('datastore'), - task: anyNamed('task'), - target: anyNamed('target'), - tags: anyNamed('tags'), - ignoreChecks: true, - ), - ).thenAnswer((_) async => false); - config.db.values[task.key] = task; - config.db.values[commit.key] = commit; - expect(() => tester.post(handler), throwsA(isA())); - }); - - test('Fails if commit does not exist', () async { - config.db.values[task.key] = task; - expect(() => tester.post(handler), throwsA(isA())); - }); - }); -} diff --git a/app_dart/test/request_handlers/reset_try_task_test.dart b/app_dart/test/request_handlers/reset_try_task_test.dart deleted file mode 100644 index 5a6adbf8c..000000000 --- a/app_dart/test/request_handlers/reset_try_task_test.dart +++ /dev/null @@ -1,99 +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:cocoon_service/src/request_handlers/reset_try_task.dart'; -import 'package:cocoon_service/src/request_handling/body.dart'; -import 'package:cocoon_service/src/request_handling/exceptions.dart'; -import 'package:github/github.dart'; -import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; - -import '../src/datastore/fake_config.dart'; -import '../src/request_handling/api_request_handler_tester.dart'; -import '../src/request_handling/fake_authentication.dart'; -import '../src/request_handling/fake_http.dart'; -import '../src/service/fake_github_service.dart'; -import '../src/service/fake_scheduler.dart'; -import '../src/utilities/entity_generators.dart'; -import '../src/utilities/mocks.dart'; - -void main() { - group('ResetTryTask', () { - late ApiRequestHandlerTester tester; - FakeClientContext clientContext; - late ResetTryTask handler; - late FakeConfig config; - FakeScheduler fakeScheduler; - FakeAuthenticatedContext authContext; - MockGitHub mockGithub; - MockPullRequestsService mockPullRequestsService; - late MockGithubChecksUtil mockGithubChecksUtil; - - setUp(() { - clientContext = FakeClientContext(); - clientContext.isDevelopmentEnvironment = false; - authContext = FakeAuthenticatedContext(clientContext: clientContext); - mockGithub = MockGitHub(); - mockPullRequestsService = MockPullRequestsService(); - config = FakeConfig(githubClient: mockGithub, githubService: FakeGithubService()); - mockGithubChecksUtil = MockGithubChecksUtil(); - tester = ApiRequestHandlerTester(context: authContext); - fakeScheduler = FakeScheduler( - config: config, - githubChecksUtil: mockGithubChecksUtil, - ); - handler = ResetTryTask( - config: config, - authenticationProvider: FakeAuthenticationProvider(clientContext: clientContext), - scheduler: fakeScheduler, - ); - when(mockGithub.pullRequests).thenReturn(mockPullRequestsService); - when(mockPullRequestsService.get(any, 123)).thenAnswer((_) async => generatePullRequest(id: 123)); - }); - - test('Empty repo', () async { - tester.request = FakeHttpRequest( - queryParametersValue: { - 'pr': '123', - }, - ); - expect(() => tester.get(handler), throwsA(isA())); - }); - - test('Empty pr', () async { - tester.request = FakeHttpRequest( - queryParametersValue: { - 'repo': 'flutter', - }, - ); - expect(() => tester.get(handler), throwsA(isA())); - }); - - test('Trigger builds if all parameters are correct', () async { - when(mockGithubChecksUtil.createCheckRun(any, any, any, any, output: anyNamed('output'))).thenAnswer((_) async { - return CheckRun.fromJson(const { - 'id': 1, - 'started_at': '2020-05-10T02:49:31Z', - 'check_suite': {'id': 2}, - }); - }); - tester.request = FakeHttpRequest( - queryParametersValue: { - ResetTryTask.kRepoParam: 'flutter', - ResetTryTask.kPullRequestNumberParam: '123', - }, - ); - expect(await tester.get(handler), Body.empty); - }); - - test('Parses empty builder correctly', () { - final List builders = handler.getBuilderList(''); - expect(builders.isEmpty, true); - }); - - test('Parses non-empty builder correctly', () { - expect(handler.getBuilderList('a, b, c'), ['a', 'b', 'c']); - }); - }); -} diff --git a/app_dart/test/request_handlers/scheduler/batch_backfiller_test.dart b/app_dart/test/request_handlers/scheduler/batch_backfiller_test.dart deleted file mode 100644 index 7f81f5f6a..000000000 --- a/app_dart/test/request_handlers/scheduler/batch_backfiller_test.dart +++ /dev/null @@ -1,284 +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/cocoon_service.dart'; -import 'package:cocoon_service/src/model/appengine/commit.dart'; -import 'package:cocoon_service/src/model/appengine/task.dart'; -import 'package:cocoon_service/src/model/ci_yaml/target.dart'; -import 'package:cocoon_service/src/model/luci/buildbucket.dart'; -import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; - -import '../../src/datastore/fake_config.dart'; -import '../../src/datastore/fake_datastore.dart'; -import '../../src/request_handling/fake_pubsub.dart'; -import '../../src/request_handling/request_handler_tester.dart'; -import '../../src/service/fake_luci_build_service.dart'; -import '../../src/service/fake_scheduler.dart'; -import '../../src/utilities/entity_generators.dart'; -import '../../src/utilities/mocks.dart'; - -final List commits = [ - generateCommit(3), - generateCommit(2), - generateCommit(1), -]; - -void main() { - late BatchBackfiller handler; - late RequestHandlerTester tester; - late FakeDatastoreDB db; - late FakePubSub pubsub; - late FakeScheduler scheduler; - late MockGithubChecksUtil mockGithubChecksUtil; - late Config config; - - group('BatchBackfiller', () { - setUp(() async { - db = FakeDatastoreDB()..addOnQuery((Iterable results) => commits); - config = FakeConfig(dbValue: db, backfillerTargetLimitValue: 2); - pubsub = FakePubSub(); - mockGithubChecksUtil = MockGithubChecksUtil(); - when( - mockGithubChecksUtil.createCheckRun( - any, - any, - any, - any, - output: anyNamed('output'), - ), - ).thenAnswer((_) async => generateCheckRun(1)); - scheduler = FakeScheduler( - config: config, - ciYaml: batchPolicyConfig, - githubChecksUtil: mockGithubChecksUtil, - luciBuildService: FakeLuciBuildService( - config: config, - pubsub: pubsub, - githubChecksUtil: mockGithubChecksUtil, - ), - ); - handler = BatchBackfiller( - config: config, - scheduler: scheduler, - ); - tester = RequestHandlerTester(); - }); - - test('does not backfill on completed task column', () async { - final List allGreen = [ - generateTask(1, name: 'Linux_android A', status: Task.statusSucceeded), - generateTask(2, name: 'Linux_android A', status: Task.statusSucceeded), - generateTask(3, name: 'Linux_android A', status: Task.statusSucceeded), - ]; - db.addOnQuery((Iterable results) => allGreen); - await tester.get(handler); - expect(pubsub.messages, isEmpty); - }); - - test('does not backfill when there is a running task', () async { - final List middleTaskInProgress = [ - generateTask(1, name: 'Linux_android A', status: Task.statusNew), - generateTask(2, name: 'Linux_android A', status: Task.statusInProgress), - generateTask(3, name: 'Linux_android A', status: Task.statusNew), - ]; - db.addOnQuery((Iterable results) => middleTaskInProgress); - await tester.get(handler); - expect(pubsub.messages, isEmpty); - }); - - test('does not backfill when task does not exist in TOT', () async { - scheduler = FakeScheduler( - config: config, - ciYaml: notInToTConfig, - githubChecksUtil: mockGithubChecksUtil, - luciBuildService: FakeLuciBuildService( - config: config, - pubsub: pubsub, - githubChecksUtil: mockGithubChecksUtil, - ), - ); - handler = BatchBackfiller( - config: config, - scheduler: scheduler, - ); - final List allGray = [ - generateTask(1, name: 'Linux_android B', status: Task.statusNew), - ]; - db.addOnQuery((Iterable results) => allGray); - await tester.get(handler); - expect(pubsub.messages.length, 0); - }); - - test('backfills latest task', () async { - final List allGray = [ - generateTask(1, name: 'Linux_android A', status: Task.statusNew), - generateTask(2, name: 'Linux_android A', status: Task.statusNew), - generateTask(3, name: 'Linux_android A', status: Task.statusNew), - ]; - db.addOnQuery((Iterable results) => allGray); - await tester.get(handler); - expect(pubsub.messages.length, 1); - final ScheduleBuildRequest scheduleBuildRequest = - (pubsub.messages.single as BatchRequest).requests!.single.scheduleBuild!; - expect(scheduleBuildRequest.priority, LuciBuildService.kBackfillPriority); - }); - - test('does not backfill targets when number of available tasks is less than BatchPolicy.kBatchSize', () async { - final List scheduleA = [ - // Linux_android A - generateTask(1, name: 'Linux_android A', status: Task.statusNew), - ]; - db.addOnQuery((Iterable results) => scheduleA); - await tester.get(handler); - expect(pubsub.messages.length, 0); - }); - - test('backfills earlier failed task with higher priority', () async { - final List allGray = [ - generateTask(1, name: 'Linux_android A', status: Task.statusNew), - generateTask(2, name: 'Linux_android A', status: Task.statusNew), - generateTask(3, name: 'Linux_android A', status: Task.statusFailed), - ]; - db.addOnQuery((Iterable results) => allGray); - await tester.get(handler); - expect(pubsub.messages.length, 1); - final ScheduleBuildRequest scheduleBuildRequest = - (pubsub.messages.single as BatchRequest).requests!.single.scheduleBuild!; - expect(scheduleBuildRequest.priority, LuciBuildService.kRerunPriority); - }); - - test('backfills task successfully with retry', () async { - pubsub.exceptionFlag = true; - pubsub.exceptionRepetition = 1; - final List allGray = [ - generateTask(1, name: 'Linux_android A', status: Task.statusNew), - generateTask(2, name: 'Linux_android A', status: Task.statusNew), - generateTask(3, name: 'Linux_android A', status: Task.statusFailed), - ]; - db.addOnQuery((Iterable results) => allGray); - await tester.get(handler); - expect(pubsub.messages.length, 1); - final ScheduleBuildRequest scheduleBuildRequest = - (pubsub.messages.single as BatchRequest).requests!.single.scheduleBuild!; - expect(scheduleBuildRequest.priority, LuciBuildService.kRerunPriority); - }); - - test('fails to backfill tasks when retry limit is hit', () async { - pubsub.exceptionFlag = true; - pubsub.exceptionRepetition = 3; - final List allGray = [ - generateTask(1, name: 'Linux_android A', status: Task.statusNew), - generateTask(2, name: 'Linux_android A', status: Task.statusNew), - generateTask(3, name: 'Linux_android A', status: Task.statusFailed), - ]; - db.addOnQuery((Iterable results) => allGray); - await tester.get(handler); - expect(pubsub.messages.length, 0); - }); - - test('backfills older task', () async { - final List oldestGray = [ - generateTask(1, name: 'Linux_android A', status: Task.statusSucceeded), - generateTask(2, name: 'Linux_android A', status: Task.statusSucceeded), - generateTask(3, name: 'Linux_android A', status: Task.statusNew), - ]; - db.addOnQuery((Iterable results) => oldestGray); - await tester.get(handler); - expect(pubsub.messages.length, 1); - }); - - test('updates task as in-progress after backfilling', () async { - final List oldestGray = [ - generateTask(1, name: 'Linux_android A', status: Task.statusSucceeded), - generateTask(2, name: 'Linux_android A', status: Task.statusSucceeded), - generateTask(3, name: 'Linux_android A', status: Task.statusNew), - ]; - db.addOnQuery((Iterable results) => oldestGray); - final Task task = oldestGray[2]; - expect(db.values.length, 0); - expect(task.status, Task.statusNew); - await tester.get(handler); - expect(db.values.length, 1); - expect(task.status, Task.statusInProgress); - }); - - test('skip scheduling builds if datastore commit fails', () async { - db.commitException = true; - final List oldestGray = [ - generateTask(1, name: 'Linux_android A', status: Task.statusSucceeded), - generateTask(2, name: 'Linux_android A', status: Task.statusSucceeded), - generateTask(3, name: 'Linux_android A', status: Task.statusNew), - ]; - db.addOnQuery((Iterable results) => oldestGray); - expect(db.values.length, 0); - await tester.get(handler); - expect(db.values.length, 0); - expect(pubsub.messages.length, 0); - }); - - test('backfills only column A when B does need backfill', () async { - final List scheduleA = [ - // Linux_android A - generateTask(1, name: 'Linux_android A', status: Task.statusSucceeded), - generateTask(2, name: 'Linux_android A', status: Task.statusSucceeded), - generateTask(3, name: 'Linux_android A', status: Task.statusNew), - // Linux_android B - generateTask(1, name: 'Linux_android B', status: Task.statusSucceeded), - generateTask(2, name: 'Linux_android B', status: Task.statusSucceeded), - generateTask(3, name: 'Linux_android B', status: Task.statusSucceeded), - ]; - db.addOnQuery((Iterable results) => scheduleA); - await tester.get(handler); - expect(pubsub.messages.length, 1); - }); - - test('backfills both column A and B', () async { - final List scheduleA = [ - // Linux_android A - generateTask(1, name: 'Linux_android A', status: Task.statusSucceeded), - generateTask(2, name: 'Linux_android A', status: Task.statusSucceeded), - generateTask(3, name: 'Linux_android A', status: Task.statusNew), - // Linux_android B - generateTask(1, name: 'Linux_android B', status: Task.statusSucceeded), - generateTask(2, name: 'Linux_android B', status: Task.statusSucceeded), - generateTask(3, name: 'Linux_android B', status: Task.statusNew), - ]; - db.addOnQuery((Iterable results) => scheduleA); - await tester.get(handler); - expect(pubsub.messages.length, 2); - }); - - test('backfills limited targets when number of available targets exceeds backfillerTargetLimit ', () async { - final List scheduleA = [ - // Linux_android A - generateTask(1, name: 'Linux_android A', status: Task.statusNew), - generateTask(2, name: 'Linux_android A', status: Task.statusNew), - // Linux_android B - generateTask(1, name: 'Linux_android B', status: Task.statusNew), - generateTask(2, name: 'Linux_android B', status: Task.statusNew), - // Linux_android C - generateTask(1, name: 'Linux_android C', status: Task.statusNew), - generateTask(2, name: 'Linux_android C', status: Task.statusNew), - ]; - db.addOnQuery((Iterable results) => scheduleA); - await tester.get(handler); - expect(pubsub.messages.length, 2); - }); - - group('getFilteredBackfill', () { - test('backfills high priorty targets first', () async { - final List> backfill = >[ - Tuple(generateTarget(1), FullTask(generateTask(1), generateCommit(1)), LuciBuildService.kRerunPriority), - Tuple(generateTarget(2), FullTask(generateTask(2), generateCommit(2)), LuciBuildService.kBackfillPriority), - Tuple(generateTarget(3), FullTask(generateTask(3), generateCommit(3)), LuciBuildService.kRerunPriority), - ]; - final List> filteredBackfill = handler.getFilteredBackfill(backfill); - expect(filteredBackfill.length, 2); - expect(filteredBackfill[0].third, LuciBuildService.kRerunPriority); - expect(filteredBackfill[1].third, LuciBuildService.kRerunPriority); - }); - }); - }); -} diff --git a/app_dart/test/request_handlers/scheduler/request_subscription_test.dart b/app_dart/test/request_handlers/scheduler/request_subscription_test.dart deleted file mode 100644 index 04283432f..000000000 --- a/app_dart/test/request_handlers/scheduler/request_subscription_test.dart +++ /dev/null @@ -1,113 +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/cocoon_service.dart'; -import 'package:cocoon_service/src/model/luci/buildbucket.dart'; -import 'package:cocoon_service/src/model/luci/push_message.dart' as push_message; -import 'package:cocoon_service/src/request_handling/exceptions.dart'; -import 'package:mockito/mockito.dart'; -import 'package:retry/retry.dart'; -import 'package:test/test.dart'; - -import '../../src/datastore/fake_config.dart'; -import '../../src/request_handling/fake_authentication.dart'; -import '../../src/request_handling/fake_http.dart'; -import '../../src/request_handling/subscription_tester.dart'; -import '../../src/utilities/mocks.dart'; - -void main() { - late SchedulerRequestSubscription handler; - late SubscriptionTester tester; - - late MockBuildBucketClient buildBucketClient; - - setUp(() async { - buildBucketClient = MockBuildBucketClient(); - when(buildBucketClient.batch(any)).thenAnswer((_) async => const BatchResponse()); - handler = SchedulerRequestSubscription( - cache: CacheService(inMemory: true), - config: FakeConfig(), - authProvider: FakeAuthenticationProvider(), - buildBucketClient: buildBucketClient, - retryOptions: const RetryOptions( - maxAttempts: 3, - maxDelay: Duration.zero, - ), - ); - tester = SubscriptionTester( - request: FakeHttpRequest(), - ); - }); - - test('throws exception when BatchRequest cannot be decoded', () async { - tester.message = const push_message.PushMessage(); - expect(() => tester.post(handler), throwsA(isA())); - }); - - test('schedules request to buildbucket', () async { - const BatchRequest request = BatchRequest(); - tester.message = push_message.PushMessage(data: base64Encode(utf8.encode(jsonEncode(request)))); - final Body body = await tester.post(handler); - expect(body, Body.empty); - }); - - test('retries schedule build if no response comes back', () async { - int attempt = 0; - when(buildBucketClient.batch(any)).thenAnswer((_) async { - attempt += 1; - if (attempt == 2) { - return const BatchResponse( - responses: [ - Response( - scheduleBuild: Build( - id: '12345', - builderId: BuilderId(builder: 'Linux A'), - ), - ), - ], - ); - } - - return const BatchResponse(); - }); - const BatchRequest request = BatchRequest( - requests: [ - Request( - scheduleBuild: ScheduleBuildRequest( - builderId: BuilderId( - builder: 'Linux A', - ), - ), - ), - ], - ); - tester.message = push_message.PushMessage(data: base64Encode(utf8.encode(jsonEncode(request)))); - final Body body = await tester.post(handler); - expect(body, Body.empty); - expect(verify(buildBucketClient.batch(any)).callCount, 2); - }); - - test('acking message and loging error when no response comes back after retry limit', () async { - when(buildBucketClient.batch(any)).thenAnswer((_) async { - return const BatchResponse(); - }); - const BatchRequest request = BatchRequest( - requests: [ - Request( - scheduleBuild: ScheduleBuildRequest( - builderId: BuilderId( - builder: 'Linux A', - ), - ), - ), - ], - ); - tester.message = push_message.PushMessage(data: base64Encode(utf8.encode(jsonEncode(request)))); - final Body body = await tester.post(handler); - expect(body, isNotNull); - expect(verify(buildBucketClient.batch(any)).callCount, 3); - }); -} diff --git a/app_dart/test/request_handlers/scheduler/vacuum_stale_tasks_test.dart b/app_dart/test/request_handlers/scheduler/vacuum_stale_tasks_test.dart deleted file mode 100644 index 3135b7961..000000000 --- a/app_dart/test/request_handlers/scheduler/vacuum_stale_tasks_test.dart +++ /dev/null @@ -1,106 +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/cocoon_service.dart'; -import 'package:cocoon_service/src/model/appengine/commit.dart'; -import 'package:cocoon_service/src/model/appengine/task.dart'; -import 'package:cocoon_service/src/service/datastore.dart'; -import 'package:gcloud/db.dart'; -import 'package:test/test.dart'; - -import '../../src/datastore/fake_config.dart'; -import '../../src/request_handling/request_handler_tester.dart'; -import '../../src/utilities/entity_generators.dart'; - -void main() { - group(VacuumStaleTasks, () { - late FakeConfig config; - late RequestHandlerTester tester; - late VacuumStaleTasks handler; - - final Commit commit = generateCommit(1); - final DateTime now = DateTime(2023, 2, 9, 13, 37); - - /// Helper function for returning test times relative to [now]. - DateTime relativeToNow(int minutes) { - final Duration duration = Duration(minutes: minutes); - - return now.subtract(duration); - } - - setUp(() { - config = FakeConfig(); - config.db.values[commit.key] = commit; - - tester = RequestHandlerTester(); - handler = VacuumStaleTasks( - config: config, - datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5), - ); - }); - - test('skips when no tasks are stale', () async { - final List expectedTasks = [ - generateTask( - 1, - status: Task.statusInProgress, - created: relativeToNow(1), - parent: commit, - ), - generateTask( - 2, - status: Task.statusSucceeded, - created: relativeToNow(VacuumStaleTasks.kTimeoutLimit.inMinutes + 5), - parent: commit, - ), - generateTask( - 3, - status: Task.statusInProgress, - created: relativeToNow(VacuumStaleTasks.kTimeoutLimit.inMinutes), - parent: commit, - ), - ]; - await config.db.commit(inserts: expectedTasks); - - await tester.get(handler); - - final List tasks = config.db.values.values.whereType().toList(); - expect(tasks, expectedTasks); - }); - - test('resets stale task', () async { - final List originalTasks = [ - generateTask( - 1, - status: Task.statusInProgress, - created: relativeToNow(1), - parent: commit, - ), - generateTask( - 2, - status: Task.statusSucceeded, - created: relativeToNow(VacuumStaleTasks.kTimeoutLimit.inMinutes + 5), - parent: commit, - ), - // Task 3 should be vacuumed - generateTask( - 3, - status: Task.statusInProgress, - created: relativeToNow(VacuumStaleTasks.kTimeoutLimit.inMinutes + 1), - parent: commit, - ), - ]; - final DatastoreService datastore = DatastoreService(config.db, 5); - await datastore.insert(originalTasks); - - await tester.get(handler); - - final List tasks = config.db.values.values.whereType().toList(); - expect(tasks[0], originalTasks[0]); - expect(tasks[1], originalTasks[1]); - expect(tasks[2].status, Task.statusNew); - expect(tasks[2].createTimestamp, 0); - }); - }); -} diff --git a/app_dart/test/request_handlers/update_existing_flaky_issues_test.dart b/app_dart/test/request_handlers/update_existing_flaky_issues_test.dart deleted file mode 100644 index 698ec38ae..000000000 --- a/app_dart/test/request_handlers/update_existing_flaky_issues_test.dart +++ /dev/null @@ -1,474 +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/cocoon_service.dart'; -import 'package:cocoon_service/src/request_handlers/flaky_handler_utils.dart'; -import 'package:cocoon_service/src/service/bigquery.dart'; -import 'package:cocoon_service/src/service/github_service.dart'; -import 'package:github/github.dart'; -import 'package:http/http.dart'; -import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; - -import '../src/datastore/fake_config.dart'; -import '../src/request_handling/api_request_handler_tester.dart'; -import '../src/request_handling/fake_authentication.dart'; -import '../src/request_handling/fake_http.dart'; -import '../src/utilities/mocks.dart'; - -import 'update_existing_flaky_issues_test_data.dart'; - -const String kThreshold = '0.02'; - -void main() { - group('Update flaky', () { - late UpdateExistingFlakyIssue handler; - late ApiRequestHandlerTester tester; - FakeHttpRequest request; - late FakeConfig config; - FakeClientContext clientContext; - FakeAuthenticationProvider auth; - late MockBigqueryService mockBigqueryService; - late MockGitHub mockGitHubClient; - late MockIssuesService mockIssuesService; - MockRepositoriesService mockRepositoriesService; - - setUp(() { - request = FakeHttpRequest( - queryParametersValue: { - FileFlakyIssueAndPR.kThresholdKey: kThreshold, - }, - ); - - clientContext = FakeClientContext(); - auth = FakeAuthenticationProvider(clientContext: clientContext); - mockBigqueryService = MockBigqueryService(); - mockGitHubClient = MockGitHub(); - mockIssuesService = MockIssuesService(); - mockRepositoriesService = MockRepositoriesService(); - - // when gets existing flaky issues. - when(mockIssuesService.listByRepo(captureAny, state: captureAnyNamed('state'), labels: captureAnyNamed('labels'))) - .thenAnswer((Invocation invocation) { - return const Stream.empty(); - }); - - // when gets the content of TESTOWNERS - when( - mockRepositoriesService.getContents( - captureAny, - kTestOwnerPath, - ), - ).thenAnswer((Invocation invocation) { - return Future.value( - RepositoryContents(file: GitHubFile(content: gitHubEncode(testOwnersContent))), - ); - }); - - when(mockGitHubClient.repositories).thenReturn(mockRepositoriesService); - when(mockGitHubClient.issues).thenReturn(mockIssuesService); - when(mockIssuesService.createComment(any, any, any)).thenAnswer((_) async => IssueComment()); - when(mockIssuesService.edit(any, any, any)).thenAnswer((_) async => Issue()); - config = FakeConfig( - githubService: GithubService(mockGitHubClient), - bigqueryService: mockBigqueryService, - githubOAuthTokenValue: 'token', - ); - tester = ApiRequestHandlerTester(request: request); - - handler = UpdateExistingFlakyIssue( - config: config, - authenticationProvider: auth, - ciYaml: testCiYaml, - ); - }); - - test('Can add existing issue comment', () async { - const int existingIssueNumber = 1234; - final List existingLabels = [ - IssueLabel(name: 'some random label'), - IssueLabel(name: 'P2'), - ]; - // When queries flaky data from BigQuery. - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) { - return Future>.value(semanticsIntegrationTestResponse); - }); - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging')) - .thenAnswer((Invocation invocation) { - return Future>.value(stagingCiyamlTestResponse); - }); - // when gets existing flaky issues. - when(mockIssuesService.listByRepo(captureAny, state: captureAnyNamed('state'), labels: captureAnyNamed('labels'))) - .thenAnswer((Invocation invocation) { - return Stream.fromIterable([ - Issue( - assignee: User(login: 'some dude'), - number: existingIssueNumber, - state: 'open', - labels: existingLabels, - title: expectedSemanticsIntegrationTestResponseTitle, - body: expectedSemanticsIntegrationTestResponseBody, - createdAt: - DateTime.now().subtract(const Duration(days: UpdateExistingFlakyIssue.kFreshPeriodForOpenFlake + 1)), - ), - ]); - }); - // when firing github request. - // This is for replacing labels. - when( - mockGitHubClient.request( - captureAny, - captureAny, - body: captureAnyNamed('body'), - ), - ).thenAnswer((Invocation invocation) { - return Future.value(Response('[]', 200)); - }); - final Map result = await utf8.decoder - .bind((await tester.get(handler)).serialize() as Stream>) - .transform(json.decoder) - .single as Map; - - // Verify comment is created correctly. - List captured = verify(mockIssuesService.createComment(captureAny, captureAny, captureAny)).captured; - expect(captured.length, 3); - expect(captured[0].toString(), Config.flutterSlug.toString()); - expect(captured[1], existingIssueNumber); - expect(captured[2], expectedSemanticsIntegrationTestIssueComment); - - // Verify labels are applied correctly. - captured = verify( - mockGitHubClient.request( - captureAny, - captureAny, - body: captureAnyNamed('body'), - ), - ).captured; - expect(captured.length, 3); - expect(captured[0].toString(), 'PUT'); - expect(captured[1], '/repos/${Config.flutterSlug.fullName}/issues/$existingIssueNumber/labels'); - expect(captured[2], GitHubJson.encode(['some random label', 'P0'])); - - expect(result['Status'], 'success'); - }); - - test('Add only one comment on existing issue when a builder has been marked as unflaky', () async { - const int existingIssueNumber = 1234; - final List existingLabels = [ - IssueLabel(name: 'some random label'), - IssueLabel(name: 'P2'), - ]; - // When queries flaky data from BigQuery. - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) { - return Future>.value(semanticsIntegrationTestResponse); - }); - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging')) - .thenAnswer((Invocation invocation) { - return Future>.value(stagingSameBuilderSemanticsIntegrationTestResponse); - }); - // when gets existing flaky issues. - when(mockIssuesService.listByRepo(captureAny, state: captureAnyNamed('state'), labels: captureAnyNamed('labels'))) - .thenAnswer((Invocation invocation) { - return Stream.fromIterable([ - Issue( - assignee: User(login: 'some dude'), - number: existingIssueNumber, - state: 'open', - labels: existingLabels, - title: expectedSemanticsIntegrationTestResponseTitle, - body: expectedSemanticsIntegrationTestResponseBody, - createdAt: - DateTime.now().subtract(const Duration(days: UpdateExistingFlakyIssue.kFreshPeriodForOpenFlake + 1)), - ), - ]); - }); - // when firing github request. - // This is for replacing labels. - when( - mockGitHubClient.request( - captureAny, - captureAny, - body: captureAnyNamed('body'), - ), - ).thenAnswer((Invocation invocation) { - return Future.value(Response('[]', 200)); - }); - final Map result = await utf8.decoder - .bind((await tester.get(handler)).serialize() as Stream>) - .transform(json.decoder) - .single as Map; - - // Verify comment is created correctly. - List captured = verify(mockIssuesService.createComment(captureAny, captureAny, captureAny)).captured; - expect(captured.length, 3); - expect(captured[0].toString(), Config.flutterSlug.toString()); - expect(captured[1], existingIssueNumber); - expect(captured[2], expectedSemanticsIntegrationTestIssueComment); - - // Verify labels are applied correctly. - captured = verify( - mockGitHubClient.request( - captureAny, - captureAny, - body: captureAnyNamed('body'), - ), - ).captured; - expect(captured.length, 3); - expect(captured[0].toString(), 'PUT'); - expect(captured[1], '/repos/${Config.flutterSlug.fullName}/issues/$existingIssueNumber/labels'); - expect(captured[2], GitHubJson.encode(['some random label', 'P0'])); - - expect(result['Status'], 'success'); - }); - - test('Can add bot staging and prod stats for a bringup: true builder', () async { - const int existingIssueNumber = 1234; - final List existingLabels = [ - IssueLabel(name: 'some random label'), - IssueLabel(name: 'P2'), - ]; - // When queries flaky data from BigQuery. - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) { - return Future>.value(ciyamlTestResponse); - }); - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging')) - .thenAnswer((Invocation invocation) { - return Future>.value(stagingCiyamlTestResponse); - }); - // when gets existing flaky issues. - when(mockIssuesService.listByRepo(captureAny, state: captureAnyNamed('state'), labels: captureAnyNamed('labels'))) - .thenAnswer((Invocation invocation) { - return Stream.fromIterable([ - Issue( - assignee: User(login: 'some dude'), - number: existingIssueNumber, - state: 'open', - labels: existingLabels, - title: expectedStagingSemanticsIntegrationTestResponseTitle, - body: expectedStagingSemanticsIntegrationTestResponseBody, - createdAt: - DateTime.now().subtract(const Duration(days: UpdateExistingFlakyIssue.kFreshPeriodForOpenFlake + 1)), - ), - ]); - }); - // when firing github request. - // This is for replacing labels. - when( - mockGitHubClient.request( - captureAny, - captureAny, - body: captureAnyNamed('body'), - ), - ).thenAnswer((Invocation invocation) { - return Future.value(Response('[]', 200)); - }); - final Map result = await utf8.decoder - .bind((await tester.get(handler)).serialize() as Stream>) - .transform(json.decoder) - .single as Map; - - // Verify comment is created correctly. - List captured = verify(mockIssuesService.createComment(captureAny, captureAny, captureAny)).captured; - expect(captured.length, 6); - expect(captured[0].toString(), Config.flutterSlug.toString()); - expect(captured[1], existingIssueNumber); - expect(captured[2], expectedCiyamlTestIssueComment); - expect(captured[3].toString(), Config.flutterSlug.toString()); - expect(captured[4], existingIssueNumber); - expect(captured[5], expectedStagingCiyamlTestIssueComment); - - // Verify labels are applied correctly. - captured = verify( - mockGitHubClient.request( - captureAny, - captureAny, - body: captureAnyNamed('body'), - ), - ).captured; - expect(captured.length, 6); - expect(captured[0].toString(), 'PUT'); - expect(captured[1], '/repos/${Config.flutterSlug.fullName}/issues/$existingIssueNumber/labels'); - expect(captured[2], GitHubJson.encode(['some random label', 'P0'])); - - expect(result['Status'], 'success'); - }); - - test('Can assign test owner', () async { - const int existingIssueNumber = 1234; - final List existingLabels = [ - IssueLabel(name: 'some random label'), - IssueLabel(name: 'P2'), - ]; - // When queries flaky data from BigQuery. - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) { - return Future>.value(semanticsIntegrationTestResponse); - }); - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging')) - .thenAnswer((Invocation invocation) { - return Future>.value(stagingCiyamlTestResponse); - }); - // when gets existing flaky issues. - when(mockIssuesService.listByRepo(captureAny, state: captureAnyNamed('state'), labels: captureAnyNamed('labels'))) - .thenAnswer((Invocation invocation) { - return Stream.fromIterable([ - Issue( - number: existingIssueNumber, - state: 'open', - labels: existingLabels, - title: expectedSemanticsIntegrationTestResponseTitle, - body: expectedSemanticsIntegrationTestResponseBody, - createdAt: - DateTime.now().subtract(const Duration(days: UpdateExistingFlakyIssue.kFreshPeriodForOpenFlake + 1)), - ), - ]); - }); - // when firing github request. - // This is for replacing labels. - when( - mockGitHubClient.request( - captureAny, - captureAny, - body: captureAnyNamed('body'), - ), - ).thenAnswer((Invocation invocation) { - return Future.value(Response('[]', 200)); - }); - - final Map result = await utf8.decoder - .bind((await tester.get(handler)).serialize() as Stream>) - .transform(json.decoder) - .single as Map; - - // Verify comment is created correctly. - final List captured = verify(mockIssuesService.edit(captureAny, captureAny, captureAny)).captured; - expect(captured.length, 3); - expect(captured[0].toString(), Config.flutterSlug.toString()); - expect(captured[1], existingIssueNumber); - final IssueRequest request = captured[2] as IssueRequest; - expect(request.assignee, 'HansMuller'); - - expect(result['Status'], 'success'); - }); - - test('Can add existing issue comment case 0.0', () async { - const int existingIssueNumber = 1234; - final List existingLabels = [ - IssueLabel(name: 'some random label'), - IssueLabel(name: 'P2'), - ]; - // When queries flaky data from BigQuery. - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) { - return Future>.value(semanticsIntegrationTestResponseZeroFlake); - }); - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging')) - .thenAnswer((Invocation invocation) { - return Future>.value(stagingCiyamlTestResponse); - }); - // when gets existing flaky issues. - when(mockIssuesService.listByRepo(captureAny, state: captureAnyNamed('state'), labels: captureAnyNamed('labels'))) - .thenAnswer((Invocation invocation) { - return Stream.fromIterable([ - Issue( - number: existingIssueNumber, - state: 'open', - labels: existingLabels, - title: expectedSemanticsIntegrationTestResponseTitle, - body: expectedSemanticsIntegrationTestResponseBody, - createdAt: - DateTime.now().subtract(const Duration(days: UpdateExistingFlakyIssue.kFreshPeriodForOpenFlake + 1)), - ), - ]); - }); - // when firing github request. - // This is for replacing labels. - when( - mockGitHubClient.request( - captureAny, - captureAny, - body: captureAnyNamed('body'), - ), - ).thenAnswer((Invocation invocation) { - return Future.value(Response('[]', 200)); - }); - - final Map result = await utf8.decoder - .bind((await tester.get(handler)).serialize() as Stream>) - .transform(json.decoder) - .single as Map; - - // Verify issue is created correctly. - List captured = verify(mockIssuesService.createComment(captureAny, captureAny, captureAny)).captured; - expect(captured.length, 3); - expect(captured[0].toString(), Config.flutterSlug.toString()); - expect(captured[1], existingIssueNumber); - expect(captured[2], expectedSemanticsIntegrationTestZeroFlakeIssueComment); - - // Verify labels are the same. - captured = verify( - mockGitHubClient.request( - captureAny, - captureAny, - body: captureAnyNamed('body'), - ), - ).captured; - expect(captured.length, 3); - expect(captured[0].toString(), 'PUT'); - expect(captured[1], '/repos/${Config.flutterSlug.fullName}/issues/$existingIssueNumber/labels'); - expect(captured[2], GitHubJson.encode(['some random label', 'P2'])); - - expect(result['Status'], 'success'); - }); - - test('Does not add comment if the issue is still fresh', () async { - const int existingIssueNumber = 1234; - final List existingLabels = [ - IssueLabel(name: 'some random label'), - IssueLabel(name: 'P2'), - ]; - // When queries flaky data from BigQuery. - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId)).thenAnswer((Invocation invocation) { - return Future>.value(semanticsIntegrationTestResponseZeroFlake); - }); - when(mockBigqueryService.listBuilderStatistic(kBigQueryProjectId, bucket: 'staging')) - .thenAnswer((Invocation invocation) { - return Future>.value(stagingCiyamlTestResponse); - }); - // when gets existing flaky issues. - when(mockIssuesService.listByRepo(captureAny, state: captureAnyNamed('state'), labels: captureAnyNamed('labels'))) - .thenAnswer((Invocation invocation) { - return Stream.fromIterable([ - Issue( - number: existingIssueNumber, - state: 'open', - labels: existingLabels, - title: expectedSemanticsIntegrationTestResponseTitle, - body: expectedSemanticsIntegrationTestResponseBody, - createdAt: - DateTime.now().subtract(const Duration(days: UpdateExistingFlakyIssue.kFreshPeriodForOpenFlake - 1)), - ), - ]); - }); - - final Map result = await utf8.decoder - .bind((await tester.get(handler)).serialize() as Stream>) - .transform(json.decoder) - .single as Map; - - verifyNever(mockIssuesService.createComment(captureAny, captureAny, captureAny)); - - // Verify labels are the same. - verifyNever( - mockGitHubClient.request( - captureAny, - captureAny, - body: captureAnyNamed('body'), - ), - ); - - expect(result['Status'], 'success'); - }); - }); -} diff --git a/app_dart/test/request_handlers/update_existing_flaky_issues_test_data.dart b/app_dart/test/request_handlers/update_existing_flaky_issues_test_data.dart deleted file mode 100644 index 5699d2b72..000000000 --- a/app_dart/test/request_handlers/update_existing_flaky_issues_test_data.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 'package:cocoon_service/src/model/ci_yaml/ci_yaml.dart'; -import 'package:cocoon_service/src/service/bigquery.dart'; -import 'package:cocoon_service/src/service/config.dart'; - -import 'package:cocoon_service/src/model/proto/protos.dart' as pb; - -const String expectedSemanticsIntegrationTestIssueComment = ''' -[prod pool] flaky ratio for the past (up to) 100 commits between 2023-06-20 and 2023-06-29 is 50.00%. Flaky number: 3; total number: 10. -One recent flaky example for a same commit: https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/103 -Commit: https://github.com/flutter/flutter/commit/abc -Flaky builds: -https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/103 -https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/102 -https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/101 - -Recent test runs: -https://flutter-dashboard.appspot.com/#/build?taskFilter=Mac_android%20android_semantics_integration_test -'''; - -const String expectedCiyamlTestIssueComment = ''' -[prod pool] flaky ratio for the past (up to) 100 commits between 2023-06-20 and 2023-06-29 is 50.00%. Flaky number: 3; total number: 10. -One recent flaky example for a same commit: https://ci.chromium.org/ui/p/flutter/builders/prod/Linux%20ci_yaml%20flutter%20roller/103 -Commit: https://github.com/flutter/flutter/commit/abc -Flaky builds: -https://ci.chromium.org/ui/p/flutter/builders/prod/Linux%20ci_yaml%20flutter%20roller/103 -https://ci.chromium.org/ui/p/flutter/builders/prod/Linux%20ci_yaml%20flutter%20roller/102 -https://ci.chromium.org/ui/p/flutter/builders/prod/Linux%20ci_yaml%20flutter%20roller/101 - -Recent test runs: -https://flutter-dashboard.appspot.com/#/build?taskFilter=Linux%20ci_yaml%20flutter%20roller -'''; - -const String expectedStagingCiyamlTestIssueComment = ''' -[staging pool] flaky ratio for the past (up to) 100 commits between 2023-06-20 and 2023-06-29 is 50.00%. Flaky number: 3; total number: 10. -One recent flaky example for a same commit: https://ci.chromium.org/ui/p/flutter/builders/staging/Linux%20ci_yaml%20flutter%20roller/103 -Commit: https://github.com/flutter/flutter/commit/abc -Flaky builds: -https://ci.chromium.org/ui/p/flutter/builders/staging/Linux%20ci_yaml%20flutter%20roller/103 -https://ci.chromium.org/ui/p/flutter/builders/staging/Linux%20ci_yaml%20flutter%20roller/102 -https://ci.chromium.org/ui/p/flutter/builders/staging/Linux%20ci_yaml%20flutter%20roller/101 - -Recent test runs: -https://flutter-dashboard.appspot.com/#/build?taskFilter=Linux%20ci_yaml%20flutter%20roller -'''; - -const String expectedSemanticsIntegrationTestZeroFlakeIssueComment = ''' -[prod pool] flaky ratio for the past (up to) 100 commits between 2023-06-20 and 2023-06-29 is 0.00%. Flaky number: 0; total number: 10. -'''; - -const String expectedSemanticsIntegrationTestNotEnoughDataComment = ''' -Current flaky ratio is not available (< 10 commits). -'''; - -final List semanticsIntegrationTestResponseZeroFlake = [ - BuilderStatistic( - name: 'Mac_android android_semantics_integration_test', - flakyRate: 0.0, - flakyBuilds: [], - succeededBuilds: ['203', '202', '201', '200', '199', '198', '197', '196', '195', '194'], - recentCommit: '', - flakyBuildOfRecentCommit: '', - flakyNumber: 0, - totalNumber: 10, - fromDate: '2023-06-20', - toDate: '2023-06-29', - ), -]; - -final List semanticsIntegrationTestResponse = [ - BuilderStatistic( - name: 'Mac_android android_semantics_integration_test', - flakyRate: 0.5, - flakyBuilds: ['103', '102', '101'], - succeededBuilds: ['203', '202', '201', '200', '199', '198', '197'], - recentCommit: 'abc', - flakyBuildOfRecentCommit: '103', - flakyNumber: 3, - totalNumber: 10, - fromDate: '2023-06-20', - toDate: '2023-06-29', - ), -]; - -final List stagingSameBuilderSemanticsIntegrationTestResponse = [ - BuilderStatistic( - name: 'Mac_android android_semantics_integration_test', - flakyRate: 0.5, - flakyBuilds: ['103', '102', '101'], - succeededBuilds: ['203', '202', '201', '200', '199', '198', '197'], - recentCommit: 'abc', - flakyBuildOfRecentCommit: '103', - flakyNumber: 3, - totalNumber: 10, - fromDate: '2023-06-20', - toDate: '2023-06-29', - ), -]; - -final List semanticsIntegrationTestResponseNotEnoughData = [ - BuilderStatistic( - name: 'Mac_android android_semantics_integration_test', - flakyRate: 0.5, - flakyBuilds: ['103', '102', '101'], - succeededBuilds: ['203', '202', '201', '200'], - recentCommit: 'abc', - flakyBuildOfRecentCommit: '103', - flakyNumber: 3, - totalNumber: 7, - fromDate: '2023-06-20', - toDate: '2023-06-29', - ), - // This builder is flakey, but it should be - // ignored because it has ignore_flakiness set. - BuilderStatistic( - name: 'Mac_android ignore_myflakiness', - flakyRate: 0.5, - flakyBuilds: ['103', '102', '101'], - succeededBuilds: ['203', '202', '201', '200'], - recentCommit: 'abc', - flakyBuildOfRecentCommit: '103', - flakyNumber: 3, - totalNumber: 7, - fromDate: '2023-06-20', - toDate: '2023-06-29', - ), -]; - -final List shardSemanticsIntegrationTestResponse = [ - BuilderStatistic( - name: 'Mac build_tests_1_4', - flakyRate: 0.5, - flakyBuilds: ['103', '102', '101'], - succeededBuilds: ['203', '202', '201', '200', '199', '198', '197'], - recentCommit: 'abc', - flakyBuildOfRecentCommit: '103', - flakyNumber: 3, - totalNumber: 10, - fromDate: '2023-06-20', - toDate: '2023-06-29', - ), -]; - -final List ciyamlTestResponse = [ - BuilderStatistic( - name: 'Linux ci_yaml flutter roller', - flakyRate: 0.5, - flakyBuilds: ['103', '102', '101'], - succeededBuilds: ['203', '202', '201', '200', '199', '198', '197'], - recentCommit: 'abc', - flakyBuildOfRecentCommit: '103', - flakyNumber: 3, - totalNumber: 10, - fromDate: '2023-06-20', - toDate: '2023-06-29', - ), -]; - -final List stagingCiyamlTestResponse = [ - BuilderStatistic( - name: 'Linux ci_yaml flutter roller', - flakyRate: 0.5, - flakyBuilds: ['103', '102', '101'], - succeededBuilds: ['203', '202', '201', '200', '199', '198', '197'], - recentCommit: 'abc', - flakyBuildOfRecentCommit: '103', - flakyNumber: 3, - totalNumber: 10, - fromDate: '2023-06-20', - toDate: '2023-06-29', - ), -]; - -const String expectedSemanticsIntegrationTestResponseTitle = - 'Mac_android android_semantics_integration_test is 50.00% flaky'; -const String expectedSemanticsIntegrationTestResponseBody = ''' - - -The post-submit test builder `Mac_android android_semantics_integration_test` had a flaky ratio 50.00% for the past 15 days, which is above our 2.00% threshold. - -One recent flaky example for a same commit: https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/103 -Commit: https://github.com/flutter/flutter/commit/abc -Flaky builds: -https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/103 -https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/102 -https://ci.chromium.org/ui/p/flutter/builders/prod/Mac_android%20android_semantics_integration_test/101 - -Recent test runs: -https://flutter-dashboard.appspot.com/#/build?taskFilter=Mac_android%20android_semantics_integration_test - -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). -'''; - -const String expectedStagingSemanticsIntegrationTestResponseTitle = 'Linux ci_yaml flutter roller is 50.00% flaky'; -const String expectedStagingSemanticsIntegrationTestResponseBody = ''' - - -The post-submit test builder `Linux ci_yaml flutter roller` had a flaky ratio 50.00% for the past 15 days, which is above our 2.00% threshold. - -One recent flaky example for a same commit: https://ci.chromium.org/ui/p/flutter/builders/prod/Linux%20ci_yaml%20flutter%20roller/103 -Commit: https://github.com/flutter/flutter/commit/abc -Flaky builds: -https://ci.chromium.org/ui/p/flutter/builders/prod/Linux%20ci_yaml%20flutter%20roller/103 -https://ci.chromium.org/ui/p/flutter/builders/prod/Linux%20ci_yaml%20flutter%20roller/102 -https://ci.chromium.org/ui/p/flutter/builders/prod/Linux%20ci_yaml%20flutter%20roller/101 - -Recent test runs: -https://flutter-dashboard.appspot.com/#/build?taskFilter=Linux%20ci_yaml_flutter%20roller - -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). -'''; - -final CiYaml testCiYaml = CiYaml( - slug: Config.flutterSlug, - branch: Config.defaultBranch(Config.flutterSlug), - config: pb.SchedulerConfig( - enabledBranches: [ - Config.defaultBranch(Config.flutterSlug), - ], - targets: [ - pb.Target( - name: 'Mac_android android_semantics_integration_test', - scheduler: pb.SchedulerSystem.luci, - presubmit: false, - properties: { - 'tags': jsonEncode(['devicelab']), - 'task_name': 'android_semantics_integration_test', - }, - ), - pb.Target( - name: 'Mac_android ignore_myflakiness', - scheduler: pb.SchedulerSystem.luci, - presubmit: false, - properties: { - 'ignore_flakiness': 'true', - 'tags': jsonEncode(['devicelab']), - 'task_name': 'ignore_myflakiness', - }, - ), - pb.Target( - name: 'Linux ci_yaml flutter roller', - scheduler: pb.SchedulerSystem.luci, - bringup: true, - timeout: 30, - runIf: ['.ci.yaml'], - recipe: 'infra/ci_yaml', - properties: { - 'tags': jsonEncode(['framework', 'hostonly', 'shard']), - }, - ), - pb.Target( - name: 'Mac build_tests_1_4', - scheduler: pb.SchedulerSystem.luci, - recipe: 'flutter/flutter_drone', - timeout: 60, - properties: { - 'add_recipes_cq': 'true', - 'shard': 'build_tests', - 'subshard': '1_4', - 'tags': jsonEncode(['framework', 'hostonly', 'shard']), - 'dependencies': jsonEncode([ - { - 'dependency': 'android_sdk', - 'version': 'version:29.0', - }, - { - 'dependency': 'chrome_and_driver', - 'version': 'version:84', - }, - { - 'dependency': 'xcode', - 'version': '13a233', - }, - { - 'dependency': 'open_jdk', - 'version': '11', - }, - { - 'dependency': 'gems', - 'version': 'v3.3.14', - }, - { - 'dependency': 'goldctl', - 'version': 'git_revision:3a77d0b12c697a840ca0c7705208e8622dc94603', - }, - ]), - }, - ), - ], - ), -); - -const String testOwnersContent = ''' - -# Below is a list of Flutter team members' GitHub handles who are -# test owners of this repository. -# -# These owners are mainly team leaders and their sub-teams. Please feel -# free to claim ownership by adding your handle to corresponding tests. -# -# This file will be used as a reference when new flaky bugs are filed and -# the TL will be assigned and the sub-team will be labeled by default -# for further triage. - -## Linux Android DeviceLab tests -/dev/devicelab/bin/tasks/android_semantics_integration_test.dart @HansMuller @flutter/framework - -## Host only framework tests -# Linux analyze -/dev/bots/analyze.dart @HansMuller @flutter/framework - -## Shards tests -# framework_tests @HansMuller @flutter/framework - - -/dev/devicelab/bin/tasks/android_semantics_integration_test.dart @HansMuller @flutter/framework -'''; - -String gitHubEncode(String source) { - final List utf8Characters = utf8.encode(source); - final String base64encoded = base64Encode(utf8Characters); - return base64encoded; -} diff --git a/app_dart/test/request_handlers/update_task_status_test.dart b/app_dart/test/request_handlers/update_task_status_test.dart deleted file mode 100644 index 9325d1f93..000000000 --- a/app_dart/test/request_handlers/update_task_status_test.dart +++ /dev/null @@ -1,174 +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/model/appengine/commit.dart'; -import 'package:cocoon_service/src/model/appengine/task.dart'; -import 'package:cocoon_service/src/request_handlers/update_task_status.dart'; -import 'package:cocoon_service/src/service/config.dart'; -import 'package:cocoon_service/src/service/datastore.dart'; -import 'package:gcloud/db.dart'; -import 'package:test/test.dart'; - -import '../src/bigquery/fake_tabledata_resource.dart'; -import '../src/datastore/fake_config.dart'; -import '../src/datastore/fake_datastore.dart'; -import '../src/request_handling/api_request_handler_tester.dart'; -import '../src/request_handling/fake_authentication.dart'; - -void main() { - group('UpdateTaskStatus', () { - late FakeConfig config; - late ApiRequestHandlerTester tester; - late UpdateTaskStatus handler; - final FakeTabledataResource tabledataResourceApi = FakeTabledataResource(); - late Commit commit; - const String commitSha = '78cbfbff4267643bb1913bc820f5ce8a3e591b40'; - const int taskId = 4506830800027648; - - setUp(() { - final FakeDatastoreDB datastoreDB = FakeDatastoreDB(); - config = FakeConfig( - dbValue: datastoreDB, - tabledataResource: tabledataResourceApi, - maxTaskRetriesValue: 2, - ); - tester = ApiRequestHandlerTester(); - handler = UpdateTaskStatus( - config: config, - authenticationProvider: FakeAuthenticationProvider(), - datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5), - ); - commit = Commit( - key: config.db.emptyKey.append(Commit, id: 'flutter/flutter/master/$commitSha'), - repository: Config.flutterSlug.fullName, - sha: commitSha, - timestamp: 123, - ); - }); - - test('TestFlaky is false when not injected', () async { - final Task task = Task( - key: commit.key.append(Task, id: taskId), - name: 'integration_ui_ios', - builderName: 'linux_integration_ui_ios', - attempts: 1, - status: Task.statusInProgress, - isFlaky: false, // mark flaky so it doesn't get auto-retried - commitKey: commit.key, - ); - config.db.values[commit.key] = commit; - config.db.values[task.key] = task; - tester.requestData = { - UpdateTaskStatus.gitBranchParam: 'master', - UpdateTaskStatus.gitShaParam: commitSha, - UpdateTaskStatus.newStatusParam: 'Failed', - UpdateTaskStatus.builderNameParam: 'linux_integration_ui_ios', - }; - - await tester.post(handler); - - expect(task.isTestFlaky, false); - }); - - test('TestFlaky is true when injected', () async { - final Task task = Task( - key: commit.key.append(Task, id: taskId), - name: 'integration_ui_ios', - builderName: 'linux_integration_ui_ios', - attempts: 1, - status: Task.statusInProgress, - isFlaky: false, // mark flaky so it doesn't get auto-retried - commitKey: commit.key, - ); - config.db.values[commit.key] = commit; - config.db.values[task.key] = task; - tester.requestData = { - UpdateTaskStatus.gitBranchParam: 'master', - UpdateTaskStatus.gitShaParam: commitSha, - UpdateTaskStatus.newStatusParam: 'Failed', - UpdateTaskStatus.builderNameParam: 'linux_integration_ui_ios', - UpdateTaskStatus.testFlayParam: true, - }; - - await tester.post(handler); - - expect(task.isTestFlaky, true); - }); - - test('task name requests can update tasks', () async { - final Task task = Task( - key: commit.key.append(Task, id: taskId), - name: 'integration_ui_ios', - builderName: 'linux_integration_ui_ios', - attempts: 1, - status: Task.statusInProgress, - isFlaky: true, // mark flaky so it doesn't get auto-retried - commitKey: commit.key, - ); - config.db.values[commit.key] = commit; - config.db.values[task.key] = task; - tester.requestData = { - UpdateTaskStatus.gitBranchParam: 'master', - UpdateTaskStatus.gitShaParam: commitSha, - UpdateTaskStatus.newStatusParam: 'Failed', - UpdateTaskStatus.builderNameParam: 'linux_integration_ui_ios', - }; - - await tester.post(handler); - - expect(task.status, 'Failed'); - expect(task.attempts, 1); - }); - - test('task name requests when task does not exists returns exception', () async { - tester.requestData = { - UpdateTaskStatus.gitBranchParam: 'master', - UpdateTaskStatus.gitShaParam: commitSha, - UpdateTaskStatus.newStatusParam: 'Failed', - UpdateTaskStatus.builderNameParam: 'linux_integration_ui_ios', - }; - expect(tester.post(handler), throwsA(isA())); - }); - - test('task name request updates when input has whitespace', () async { - config.db.values[commit.key] = commit; - final Task cocoonTask = Task( - key: commit.key.append(Task, id: taskId), - name: 'integration_ui_ios', - attempts: 0, - isFlaky: true, // mark flaky so it doesn't get auto-retried - commitKey: commit.key, - status: Task.statusNew, - ); - config.db.values[cocoonTask.key] = cocoonTask; - final Task luciTask = Task( - key: commit.key.append(Task, id: taskId), - name: 'integration_ui_ios', - builderName: 'linux_integration_ui_ios', - attempts: 1, - status: Task.statusInProgress, - isFlaky: true, // mark flaky so it doesn't get auto-retried - commitKey: commit.key, - ); - config.db.values[luciTask.key] = luciTask; - const int asciiLF = 10; - final List branchChars = List.from('master'.codeUnits)..add(asciiLF); - final List shaChars = List.from(commitSha.codeUnits)..add(asciiLF); - tester.requestData = { - UpdateTaskStatus.gitBranchParam: String.fromCharCodes(branchChars), - UpdateTaskStatus.gitShaParam: String.fromCharCodes(shaChars), - UpdateTaskStatus.newStatusParam: 'Failed', - UpdateTaskStatus.builderNameParam: 'linux_integration_ui_ios', - }; - - await tester.post(handler); - - expect(luciTask.status, Task.statusFailed); - expect(luciTask.attempts, 1); - - expect(cocoonTask.status, Task.statusNew); - expect(cocoonTask.attempts, 0); - }); - }); -} diff --git a/app_dart/test/request_handlers/vacuum_github_commits_test.dart b/app_dart/test/request_handlers/vacuum_github_commits_test.dart deleted file mode 100644 index 5747e41f5..000000000 --- a/app_dart/test/request_handlers/vacuum_github_commits_test.dart +++ /dev/null @@ -1,182 +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/model/appengine/commit.dart'; -import 'package:cocoon_service/src/request_handlers/vacuum_github_commits.dart'; -import 'package:cocoon_service/src/request_handling/body.dart'; -import 'package:cocoon_service/src/service/config.dart'; -import 'package:cocoon_service/src/service/datastore.dart'; -import 'package:gcloud/db.dart' as gcloud_db; -import 'package:gcloud/db.dart'; -import 'package:github/github.dart'; -import 'package:googleapis/bigquery/v2.dart'; -import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; - -import '../src/datastore/fake_config.dart'; -import '../src/datastore/fake_datastore.dart'; -import '../src/request_handling/api_request_handler_tester.dart'; -import '../src/request_handling/fake_authentication.dart'; -import '../src/service/fake_github_service.dart'; -import '../src/service/fake_scheduler.dart'; -import '../src/utilities/mocks.dart'; - -void main() { - group('VacuumGithubCommits', () { - late FakeConfig config; - FakeAuthenticationProvider auth; - late FakeDatastoreDB db; - FakeScheduler scheduler; - late ApiRequestHandlerTester tester; - late VacuumGithubCommits handler; - - late List githubCommits; - late int yieldedCommitCount; - - List commitList() { - final List commits = []; - for (String sha in githubCommits) { - final User author = User() - ..login = 'Username' - ..avatarUrl = 'http://example.org/avatar.jpg'; - final GitCommitUser committer = - GitCommitUser('Username', 'Username@abc.com', DateTime.fromMillisecondsSinceEpoch(int.parse(sha))); - final GitCommit gitCommit = GitCommit() - ..message = 'commit message' - ..committer = committer; - commits.add( - RepositoryCommit() - ..sha = sha - ..author = author - ..commit = gitCommit, - ); - } - return commits; - } - - Commit shaToCommit(String sha, String branch, RepositorySlug slug) { - return Commit( - key: db.emptyKey.append(Commit, id: '${slug.fullName}/$branch/$sha'), - repository: slug.fullName, - sha: sha, - branch: branch, - timestamp: int.parse(sha), - ); - } - - setUp(() { - final MockRepositoriesService repositories = MockRepositoriesService(); - final FakeGithubService githubService = FakeGithubService(); - final MockTabledataResource tabledataResourceApi = MockTabledataResource(); - when(tabledataResourceApi.insertAll(any, any, any, any)).thenAnswer((_) async { - return TableDataInsertAllResponse(); - }); - - yieldedCommitCount = 0; - db = FakeDatastoreDB(); - config = FakeConfig( - tabledataResource: tabledataResourceApi, - githubService: githubService, - dbValue: db, - supportedBranchesValue: [ - 'master', - 'main', - ], - supportedReposValue: { - Config.cocoonSlug, - Config.engineSlug, - Config.flutterSlug, - Config.packagesSlug, - }, - ); - - auth = FakeAuthenticationProvider(); - scheduler = FakeScheduler( - config: config, - ciYaml: exampleConfig, - ); - tester = ApiRequestHandlerTester(); - handler = VacuumGithubCommits( - config: config, - authenticationProvider: auth, - datastoreProvider: (DatastoreDB db) => DatastoreService(config.db, 5), - scheduler: scheduler, - ); - - githubService.listCommitsBranch = (String branch, int hours) { - return commitList(); - }; - - when(githubService.github.repositories).thenReturn(repositories); - }); - - test('succeeds when GitHub returns no commits', () async { - githubCommits = []; - config.supportedBranchesValue = ['master']; - final Body body = await tester.get(handler); - expect(yieldedCommitCount, 0); - expect(db.values, isEmpty); - expect(await body.serialize().toList(), isEmpty); - }); - - test('does not fail on empty commit list', () async { - githubCommits = []; - expect(db.values.values.whereType().length, 0); - await tester.get(handler); - expect(db.values.values.whereType().length, 0); - }); - - test('does not add recent commits', () async { - githubCommits = ['${DateTime.now().millisecondsSinceEpoch}']; - - expect(db.values.values.whereType().length, 0); - await tester.get(handler); - expect(db.values.values.whereType().length, 0); - }); - - test('inserts all relevant fields of the commit', () async { - githubCommits = ['1']; - expect(db.values.values.whereType().length, 0); - await tester.get(handler); - expect(db.values.values.whereType().length, config.supportedRepos.length); - final List commits = db.values.values.whereType().toList(); - final Commit commit = commits.first; - expect(commit.repository, 'flutter/cocoon'); - expect(commit.branch, 'main'); - expect(commit.sha, '1'); - expect(commit.timestamp, 1); - expect(commit.author, 'Username'); - expect(commit.authorAvatarUrl, 'http://example.org/avatar.jpg'); - expect(commit.message, 'commit message'); - expect(commits[1].repository, Config.engineSlug.fullName); - expect(commits[2].repository, Config.flutterSlug.fullName); - }); - - test('skips commits for which transaction commit fails', () async { - githubCommits = ['2', '3', '4']; - - /// This test is simulating an existing branch, which must already - /// have at least one commit in the datastore. - final Commit commit = shaToCommit('1', 'main', Config.engineSlug); - db.values[commit.key] = commit; - - db.onCommit = (List> inserts, List> deletes) { - if (inserts.whereType().where((Commit commit) => commit.sha == '3').isNotEmpty) { - throw StateError('Commit failed'); - } - }; - final Body body = await tester.get(handler); - - /// The +1 is coming from the engine repository and manually added commit on the top of this test. - expect(db.values.values.whereType().length, 8 + 1); // 2 commits for 4 repos - expect(db.values.values.whereType().map(toSha), containsAll(['1', '2', '4'])); - expect(db.values.values.whereType().map(toTimestamp), containsAll([1, 2, 4])); - expect(await body.serialize().toList(), isEmpty); - }); - }); -} - -String toSha(Commit commit) => commit.sha!; - -int toTimestamp(Commit commit) => commit.timestamp!; diff --git a/app_dart/test/request_handling/api_request_handler_test.dart b/app_dart/test/request_handling/api_request_handler_test.dart deleted file mode 100644 index fd218ee4e..000000000 --- a/app_dart/test/request_handling/api_request_handler_test.dart +++ /dev/null @@ -1,177 +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 'dart:io'; - -import 'package:cocoon_service/src/request_handling/api_request_handler.dart'; -import 'package:cocoon_service/src/request_handling/body.dart'; -import 'package:cocoon_service/src/service/logging.dart'; -import 'package:gcloud/service_scope.dart' as ss; -import 'package:logging/logging.dart'; -import 'package:test/test.dart'; - -import '../src/datastore/fake_config.dart'; -import '../src/request_handling/fake_authentication.dart'; - -void main() { - group('ApiRequestHandler', () { - late HttpServer server; - late ApiRequestHandler handler; - - final List records = []; - - setUpAll(() async { - server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0); - server.listen((HttpRequest request) { - final ZoneSpecification spec = ZoneSpecification( - print: (Zone self, ZoneDelegate parent, Zone zone, String line) { - log.fine(line); - }, - ); - runZoned( - () { - return ss.fork(() { - ss.register(#appengine.logging, log); - return handler.service(request); - }); - }, - zoneSpecification: spec, - ); - }); - }); - - tearDownAll(() async { - await server.close(); - }); - - setUp(() { - records.clear(); - log.onRecord.listen((LogRecord record) => records.add(record)); - }); - - Future issueRequest({String? body}) async { - final HttpClient client = HttpClient(); - final Uri url = Uri(scheme: 'http', host: 'localhost', port: server.port, path: '/path'); - final HttpClientRequest request = await client.getUrl(url); - if (body != null) { - request.contentLength = body.length; - request.write(body); - await request.flush(); - } - return request.close(); - } - - test('failed authentication yields HTTP unauthorized', () async { - handler = Unauth(); - final HttpClientResponse response = await issueRequest(); - expect(response.statusCode, HttpStatus.unauthorized); - expect(await utf8.decoder.bind(response).join(), 'Not authenticated'); - expect(records, isEmpty); - }); - - test('empty request body yields empty requestData', () async { - handler = ReadParams(); - final HttpClientResponse response = await issueRequest(); - expect(response.headers.value('X-Test-RequestBody'), '[]'); - expect(response.headers.value('X-Test-RequestData'), '{}'); - expect(response.statusCode, HttpStatus.ok); - expect(await response.toList(), isEmpty); - expect(records, isEmpty); - }); - - test('JSON request body yields valid requestData', () async { - handler = ReadParams(); - final HttpClientResponse response = await issueRequest(body: '{"param1":"value1"}'); - expect(response.headers.value('X-Test-RequestBody'), isNotEmpty); - expect(response.headers.value('X-Test-RequestData'), '{param1: value1}'); - expect(response.statusCode, HttpStatus.ok); - expect(await response.toList(), isEmpty); - expect(records, isEmpty); - }); - - test('non-JSON request body yields HTTP ok', () async { - handler = ReadParams(); - final HttpClientResponse response = await issueRequest(body: 'abc'); - expect(response.statusCode, HttpStatus.ok); - expect(response.headers.value('X-Test-RequestBody'), '[97, 98, 99]'); - expect(response.headers.value('X-Test-RequestData'), '{}'); - expect(await response.toList(), isEmpty); - expect(records, isEmpty); - }); - - test('can access authContext', () async { - handler = AccessAuth(); - final HttpClientResponse response = await issueRequest(); - expect(response.headers.value('X-Test-IsDev'), 'true'); - expect(response.statusCode, HttpStatus.ok); - expect(records, isEmpty); - }); - - test('missing required request parameters yields HTTP bad request', () async { - handler = NeedsParams(); - HttpClientResponse response = await issueRequest(); - expect(response.statusCode, HttpStatus.badRequest); - response = await issueRequest(body: '{"param1":"value1"}'); - expect(response.statusCode, HttpStatus.badRequest); - response = await issueRequest(body: '{"param2":"value2"}'); - expect(response.statusCode, HttpStatus.badRequest); - response = await issueRequest(body: '{"param1":"value1","param2":"value2"}'); - expect(response.statusCode, HttpStatus.ok); - response = await issueRequest(body: '{"param1":"value1","param2":"value2","extra":"yes"}'); - expect(response.statusCode, HttpStatus.ok); - expect(records, isEmpty); - }); - }); -} - -class Unauth extends ApiRequestHandler { - Unauth() - : super( - config: FakeConfig(), - authenticationProvider: FakeAuthenticationProvider(authenticated: false), - ); - - @override - Future get() async => throw StateError('Unreachable'); -} - -class Auth extends ApiRequestHandler { - Auth() : super(config: FakeConfig(), authenticationProvider: FakeAuthenticationProvider()); - - @override - Future get() async => Body.empty; -} - -class ReadParams extends ApiRequestHandler { - ReadParams() : super(config: FakeConfig(), authenticationProvider: FakeAuthenticationProvider()); - - @override - Future get() async { - response!.headers.add('X-Test-RequestBody', requestBody.toString()); - response!.headers.add('X-Test-RequestData', requestData.toString()); - return Body.empty; - } -} - -class NeedsParams extends ApiRequestHandler { - NeedsParams() : super(config: FakeConfig(), authenticationProvider: FakeAuthenticationProvider()); - - @override - Future get() async { - checkRequiredParameters(['param1', 'param2']); - return Body.empty; - } -} - -class AccessAuth extends ApiRequestHandler { - AccessAuth() : super(config: FakeConfig(), authenticationProvider: FakeAuthenticationProvider()); - - @override - Future get() async { - response!.headers.add('X-Test-IsDev', authContext!.clientContext.isDevelopmentEnvironment); - return Body.empty; - } -} diff --git a/app_dart/test/request_handling/authentication_test.dart b/app_dart/test/request_handling/authentication_test.dart deleted file mode 100644 index 20a375fd6..000000000 --- a/app_dart/test/request_handling/authentication_test.dart +++ /dev/null @@ -1,173 +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 'package:cocoon_service/src/model/appengine/allowed_account.dart'; -import 'package:cocoon_service/src/model/google/token_info.dart'; -import 'package:cocoon_service/src/request_handling/authentication.dart'; -import 'package:cocoon_service/src/request_handling/exceptions.dart'; -import 'package:cocoon_service/src/service/logging.dart'; -import 'package:http/http.dart' as http; -import 'package:http/testing.dart'; -import 'package:logging/logging.dart'; -import 'package:test/test.dart'; - -import '../src/datastore/fake_config.dart'; -import '../src/request_handling/fake_authentication.dart'; -import '../src/request_handling/fake_http.dart'; - -void main() { - group('AuthenticationProvider', () { - late FakeConfig config; - late FakeClientContext clientContext; - late FakeHttpRequest request; - late AuthenticationProvider auth; - late TokenInfo token; - - setUp(() { - config = FakeConfig(); - token = TokenInfo(email: 'abc123@gmail.com', issued: DateTime.now()); - clientContext = FakeClientContext(); - request = FakeHttpRequest(); - auth = AuthenticationProvider( - config: config, - clientContextProvider: () => clientContext, - httpClientProvider: () => throw AssertionError(), - ); - }); - - test('throws Unauthenticated with no auth headers', () async { - expect(auth.authenticate(request), throwsA(isA())); - }); - - test('succeeds for App Engine cronjobs', () async { - request.headers.set('X-Appengine-Cron', 'true'); - final AuthenticatedContext result = await auth.authenticate(request); - expect(result.clientContext, same(clientContext)); - }); - - group('when id token is given', () { - late MockClient httpClient; - - setUp(() { - auth = AuthenticationProvider( - config: config, - clientContextProvider: () => clientContext, - httpClientProvider: () => httpClient, - ); - }); - - test('auth succeeds with authenticated header', () async { - httpClient = MockClient((_) async => http.Response('{"aud": "client-id", "hd": "google.com"}', HttpStatus.ok)); - auth = AuthenticationProvider( - config: config, - clientContextProvider: () => clientContext, - httpClientProvider: () => httpClient, - ); - config.oauthClientIdValue = 'client-id'; - request.headers.add('X-Flutter-IdToken', 'authenticated'); - final AuthenticatedContext result = await auth.authenticate(request); - expect(result.clientContext, same(clientContext)); - expect(result, isNotNull); - }); - - test('fails if token verification fails', () async { - config.oauthClientIdValue = 'client-id'; - request.headers.add('X-Flutter-IdToken', 'authenticated'); - await expectLater( - auth.authenticateToken( - token, - clientContext: FakeClientContext(), - ), - throwsA(isA()), - ); - }); - - test('fails if tokenInfo returns invalid JSON', () async { - httpClient = MockClient((_) async => http.Response('Not JSON!', HttpStatus.ok)); - final List records = []; - log.onRecord.listen((LogRecord record) => records.add(record)); - await expectLater(auth.tokenInfo(request), throwsA(isA())); - expect(records, isEmpty); - }); - - test('fails if token verification yields forged token', () async { - final TokenInfo token = TokenInfo( - audience: 'forgery', - email: 'abc@abc.com', - issued: DateTime.now(), - ); - config.oauthClientIdValue = 'expected-client-id'; - await expectLater( - auth.authenticateToken( - token, - clientContext: FakeClientContext(), - ), - throwsA(isA()), - ); - }); - - test('allows different aud for gcloud tokens with google accounts', () async { - final TokenInfo token = TokenInfo( - audience: 'different', - email: 'abc@google.com', - issued: DateTime.now(), - ); - config.oauthClientIdValue = 'expected-client-id'; - await expectLater( - auth.authenticateToken( - token, - clientContext: FakeClientContext(), - ), - throwsA(isA()), - ); - }); - - test('succeeds for google.com auth user', () async { - final TokenInfo token = TokenInfo( - audience: 'client-id', - hostedDomain: 'google.com', - email: 'abc@google.com', - issued: DateTime.now(), - ); - config.oauthClientIdValue = 'client-id'; - final AuthenticatedContext result = await auth.authenticateToken( - token, - clientContext: clientContext, - ); - expect(result.clientContext, same(clientContext)); - }); - - test('fails for non-allowed non-Google auth users', () async { - final TokenInfo token = - TokenInfo(audience: 'client-id', hostedDomain: 'gmail.com', email: 'abc@gmail.com', issued: DateTime.now()); - config.oauthClientIdValue = 'client-id'; - await expectLater( - auth.authenticateToken( - token, - clientContext: FakeClientContext(), - ), - throwsA(isA()), - ); - }); - - test('succeeds for allowed non-Google auth users', () async { - final AllowedAccount account = AllowedAccount( - key: config.db.emptyKey.append(AllowedAccount, id: 123), - email: 'test@gmail.com', - ); - config.db.values[account.key] = account; - final TokenInfo token = TokenInfo( - audience: 'client-id', - email: 'test@gmail.com', - issued: DateTime.now(), - ); - config.oauthClientIdValue = 'client-id'; - final AuthenticatedContext result = await auth.authenticateToken(token, clientContext: clientContext); - expect(result.clientContext, same(clientContext)); - }); - }); - }); -} diff --git a/app_dart/test/request_handling/cache_request_handler_test.dart b/app_dart/test/request_handling/cache_request_handler_test.dart deleted file mode 100644 index 35484aed3..000000000 --- a/app_dart/test/request_handling/cache_request_handler_test.dart +++ /dev/null @@ -1,108 +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:cocoon_service/src/request_handling/body.dart'; -import 'package:cocoon_service/src/request_handling/cache_request_handler.dart'; -import 'package:cocoon_service/src/service/cache_service.dart'; -import 'package:test/test.dart'; - -import '../src/datastore/fake_config.dart'; -import '../src/request_handling/fake_http.dart'; -import '../src/request_handling/fake_request_handler.dart'; -import '../src/request_handling/request_handler_tester.dart'; - -void main() { - group('CacheRequestHandler', () { - late FakeConfig config; - late RequestHandlerTester tester; - - late CacheService cache; - - const String testHttpPath = '/cache_request_handler_test'; - - setUp(() async { - config = FakeConfig(); - tester = RequestHandlerTester( - request: FakeHttpRequest( - path: testHttpPath, - ), - ); - - cache = CacheService(inMemory: true); - }); - - test('returns response from cache', () async { - const String responseKey = '$testHttpPath:'; - const String expectedResponse = 'Hello, World!'; - final Body expectedBody = Body.forString(expectedResponse); - final FakeRequestHandler fallbackHandler = FakeRequestHandler(body: expectedBody, config: FakeConfig()); - - final Uint8List? serializedBody = await expectedBody.serialize().first; - - await cache.set(CacheRequestHandler.responseSubcacheName, responseKey, serializedBody); - - final CacheRequestHandler cacheRequestHandler = - CacheRequestHandler(delegate: fallbackHandler, cache: cache, config: config); - - final Body body = await tester.get(cacheRequestHandler); - final Uint8List response = (await body.serialize().first)!; - final String strResponse = utf8.decode(response); - expect(strResponse, expectedResponse); - }); - - test('fallback handler called when cache is empty', () async { - final FakeRequestHandler fallbackHandler = FakeRequestHandler( - body: Body.forString('hello!'), - config: FakeConfig(), - ); - - final CacheRequestHandler cacheRequestHandler = - CacheRequestHandler(delegate: fallbackHandler, cache: cache, config: config); - - expect(fallbackHandler.callCount, 0); - await tester.get(cacheRequestHandler); - expect(fallbackHandler.callCount, 1); - }); - - test('flush cache param calls purge', () async { - tester = RequestHandlerTester( - request: FakeHttpRequest( - path: testHttpPath, - queryParametersValue: { - CacheRequestHandler.flushCacheQueryParam: 'true', - }, - ), - ); - final FakeRequestHandler fallbackHandler = FakeRequestHandler( - body: Body.empty, - config: FakeConfig(), - ); - - const String responseKey = '$testHttpPath:'; - const String expectedResponse = 'Hello, World!'; - final Body expectedBody = Body.forString(expectedResponse); - - final Uint8List? serializedBody = await expectedBody.serialize().first; - - // set an existing response for the request - await cache.set(CacheRequestHandler.responseSubcacheName, responseKey, serializedBody); - - final CacheRequestHandler cacheRequestHandler = - CacheRequestHandler(delegate: fallbackHandler, cache: cache, config: config); - - expect(fallbackHandler.callCount, 0); - - final Body body = await tester.get(cacheRequestHandler); - final Uint8List response = (await body.serialize().first)!; - final String strResponse = utf8.decode(response); - - // the mock should update the cache to have null -> empty string - expect(strResponse, ''); - expect(fallbackHandler.callCount, 1); - }); - }); -} diff --git a/app_dart/test/request_handling/pubsub_authentication_test.dart b/app_dart/test/request_handling/pubsub_authentication_test.dart deleted file mode 100644 index ba1170a8f..000000000 --- a/app_dart/test/request_handling/pubsub_authentication_test.dart +++ /dev/null @@ -1,145 +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 'package:cocoon_service/src/request_handling/authentication.dart' show AuthenticatedContext; -import 'package:cocoon_service/src/request_handling/exceptions.dart'; -import 'package:cocoon_service/src/request_handling/pubsub_authentication.dart'; -import 'package:cocoon_service/src/service/config.dart'; -import 'package:http/http.dart' as http; -import 'package:http/testing.dart'; -import 'package:test/test.dart'; - -import '../src/datastore/fake_config.dart'; -import '../src/request_handling/fake_authentication.dart'; -import '../src/request_handling/fake_http.dart'; - -void main() { - group('PubsubAuthenticationProvider', () { - late FakeConfig config; - late FakeClientContext clientContext; - late FakeHttpRequest request; - late PubsubAuthenticationProvider auth; - late MockClient httpClient; - - setUp(() { - config = FakeConfig(); - clientContext = FakeClientContext(); - request = FakeHttpRequest(); - auth = PubsubAuthenticationProvider( - config: config, - clientContextProvider: () => clientContext, - httpClientProvider: () => httpClient, - ); - }); - - for (String allowedAccount in Config.allowedPubsubServiceAccounts) { - test('auth succeeds for $allowedAccount', () async { - httpClient = MockClient( - (_) async => http.Response( - _generateTokenResponse(allowedAccount), - HttpStatus.ok, - headers: { - HttpHeaders.contentTypeHeader: 'application/json', - }, - ), - ); - auth = PubsubAuthenticationProvider( - config: config, - clientContextProvider: () => clientContext, - httpClientProvider: () => httpClient, - ); - - request.headers.add(HttpHeaders.authorizationHeader, 'Bearer token'); - - final AuthenticatedContext result = await auth.authenticate(request); - expect(result.clientContext, same(clientContext)); - }); - } - - test('auth fails with unauthorized service account', () async { - httpClient = MockClient( - (_) async => http.Response( - _generateTokenResponse('unauthorized@gmail.com'), - HttpStatus.ok, - headers: { - HttpHeaders.contentTypeHeader: 'application/json', - }, - ), - ); - auth = PubsubAuthenticationProvider( - config: config, - clientContextProvider: () => clientContext, - httpClientProvider: () => httpClient, - ); - - request.headers.add(HttpHeaders.authorizationHeader, 'Bearer token'); - - expect(auth.authenticate(request), throwsA(isA())); - }); - - test('auth fails with invalid token', () async { - httpClient = MockClient( - (_) async => http.Response( - 'Invalid token', - HttpStatus.unauthorized, - headers: { - HttpHeaders.contentTypeHeader: 'application/json', - }, - ), - ); - auth = PubsubAuthenticationProvider( - config: config, - clientContextProvider: () => clientContext, - httpClientProvider: () => httpClient, - ); - - request.headers.add(HttpHeaders.authorizationHeader, 'Bearer token'); - - expect(auth.authenticate(request), throwsA(isA())); - }); - - test('auth fails with expired token', () async { - httpClient = MockClient( - (_) async => http.Response( - _generateTokenResponse( - Config.allowedPubsubServiceAccounts.first, - expiresIn: -1, - ), - HttpStatus.ok, - headers: { - HttpHeaders.contentTypeHeader: 'application/json', - }, - ), - ); - auth = PubsubAuthenticationProvider( - config: config, - clientContextProvider: () => clientContext, - httpClientProvider: () => httpClient, - ); - - request.headers.add(HttpHeaders.authorizationHeader, 'Bearer token'); - - expect(auth.authenticate(request), throwsA(isA())); - }); - }); -} - -/// Return Google's OAuth response. -String _generateTokenResponse( - String email, { - int expiresIn = 123, -}) { - return '''{ - "issued_to": "456", - "audience": "https://flutter-dashboard.appspot.com/api/luci-status-handler", - "user_id": "789", - "expires_in": $expiresIn, - "email": "$email", - "verified_email": true, - "issuer": "https://accounts.google.com", - "issued_at": 412321 - }'''; -} diff --git a/app_dart/test/request_handling/request_handler_test.dart b/app_dart/test/request_handling/request_handler_test.dart deleted file mode 100644 index 269df14e1..000000000 --- a/app_dart/test/request_handling/request_handler_test.dart +++ /dev/null @@ -1,212 +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 'dart:io'; - -import 'package:cocoon_service/src/request_handling/body.dart'; -import 'package:cocoon_service/src/request_handling/exceptions.dart'; -import 'package:cocoon_service/src/request_handling/request_handler.dart'; -import 'package:cocoon_service/src/service/logging.dart'; -import 'package:gcloud/service_scope.dart' as ss; -import 'package:logging/logging.dart'; -import 'package:test/test.dart'; - -import '../src/datastore/fake_config.dart'; - -void main() { - group('RequestHandler', () { - late HttpServer server; - late RequestHandler handler; - - final List records = []; - - setUpAll(() async { - server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0); - server.listen((HttpRequest request) { - runZoned(() { - return ss.fork(() { - return handler.service(request); - }); - }); - }); - }); - - tearDownAll(() async { - await server.close(); - }); - - setUp(() { - records.clear(); - log.onRecord.listen((LogRecord record) => records.add(record)); - }); - - Future issueRequest(String method) async { - final HttpClient client = HttpClient(); - final Uri url = Uri(scheme: 'http', host: 'localhost', port: server.port, path: '/path'); - final HttpClientRequest request = await client.openUrl(method, url); - return request.close(); - } - - Future issueGet() => issueRequest('get'); - - Future issuePost() => issueRequest('post'); - - test('Unimplemented methods yield HTTP method not allowed', () async { - handler = MethodNotAllowed(); - HttpClientResponse response = await issueGet(); - expect(response.statusCode, HttpStatus.methodNotAllowed); - response = await issuePost(); - expect(response.statusCode, HttpStatus.methodNotAllowed); - expect(records, isEmpty); - }); - - test('empty body yields empty HTTP response body', () async { - handler = EmptyBodyHandler(); - final HttpClientResponse response = await issueGet(); - expect(response.statusCode, HttpStatus.ok); - expect(await response.toList(), isEmpty); - expect(records, isEmpty); - }); - - test('string body yields string HTTP response body', () async { - handler = StringBodyHandler(); - final HttpClientResponse response = await issueGet(); - expect(response.statusCode, HttpStatus.ok); - expect(await utf8.decoder.bind(response).join(), 'Hello world'); - expect(records, isEmpty); - }); - - test('JsonBody yields JSON HTTP response body', () async { - handler = JsonBodyHandler(); - final HttpClientResponse response = await issueGet(); - expect(response.statusCode, HttpStatus.ok); - expect(await utf8.decoder.bind(response).join(), '{"key":"value"}'); - expect(records, isEmpty); - }); - - test('throwing HttpException yields corresponding HTTP status', () async { - handler = ThrowsHttpException(); - final HttpClientResponse response = await issueGet(); - expect(response.statusCode, HttpStatus.badRequest); - expect(await utf8.decoder.bind(response).join(), 'Bad request'); - expect(records, isEmpty); - }); - - test('throwing general exception yields HTTP 500 and logs to server logs', () async { - handler = ThrowsStateError(); - final HttpClientResponse response = await issueGet(); - expect(response.statusCode, HttpStatus.internalServerError); - expect(await utf8.decoder.bind(response).join(), contains('error message')); - expect(records.first.message, contains('error message')); - }); - - test('may access the request and response directly', () async { - handler = AccessesRequestAndResponseDirectly(); - final HttpClientResponse response = await issueGet(); - expect(response.headers.value('X-Test-Path'), '/path'); - expect(records, isEmpty); - }); - - test('may implement both GET and POST', () async { - handler = ImplementsBothGetAndPost(); - HttpClientResponse response = await issueGet(); - expect(response.headers.value('X-Test-Get'), 'true'); - expect(response.headers.value('X-Test-Post'), isNull); - response = await issuePost(); - expect(response.headers.value('X-Test-Get'), isNull); - expect(response.headers.value('X-Test-Post'), 'true'); - expect(records, isEmpty); - }); - - test('may implement only POST', () async { - handler = ImplementsOnlyPost(); - HttpClientResponse response = await issueGet(); - expect(response.statusCode, HttpStatus.methodNotAllowed); - response = await issuePost(); - expect(response.statusCode, HttpStatus.ok); - expect(records, isEmpty); - }); - }); -} - -class TestBody extends JsonBody { - const TestBody(); - - @override - Map toJson() => const {'key': 'value'}; -} - -class MethodNotAllowed extends RequestHandler { - MethodNotAllowed() : super(config: FakeConfig()); -} - -class EmptyBodyHandler extends RequestHandler { - EmptyBodyHandler() : super(config: FakeConfig()); - - @override - Future get() async => Body.empty; -} - -class StringBodyHandler extends RequestHandler { - StringBodyHandler() : super(config: FakeConfig()); - - @override - Future get() async => Body.forString('Hello world'); -} - -class JsonBodyHandler extends RequestHandler { - JsonBodyHandler() : super(config: FakeConfig()); - - @override - Future get() async => const TestBody(); -} - -class ThrowsHttpException extends RequestHandler { - ThrowsHttpException() : super(config: FakeConfig()); - - @override - Future get() async => throw const BadRequestException(); -} - -class ThrowsStateError extends RequestHandler { - ThrowsStateError() : super(config: FakeConfig()); - - @override - Future get() async => throw StateError('error message'); -} - -class AccessesRequestAndResponseDirectly extends RequestHandler { - AccessesRequestAndResponseDirectly() : super(config: FakeConfig()); - - @override - Future get() async { - response!.headers.add('X-Test-Path', request!.uri.path); - return Body.empty; - } -} - -class ImplementsBothGetAndPost extends RequestHandler { - ImplementsBothGetAndPost() : super(config: FakeConfig()); - - @override - Future get() async { - response!.headers.add('X-Test-Get', 'true'); - return Body.empty; - } - - @override - Future post() async { - response!.headers.add('X-Test-Post', 'true'); - return Body.empty; - } -} - -class ImplementsOnlyPost extends RequestHandler { - ImplementsOnlyPost() : super(config: FakeConfig()); - - @override - Future post() async => Body.empty; -} diff --git a/app_dart/test/request_handling/static_file_handler_test.dart b/app_dart/test/request_handling/static_file_handler_test.dart deleted file mode 100644 index 92cd3104b..000000000 --- a/app_dart/test/request_handling/static_file_handler_test.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:convert' show utf8; - -import 'package:cocoon_service/cocoon_service.dart'; -import 'package:cocoon_service/src/request_handling/exceptions.dart'; -import 'package:file/file.dart'; -import 'package:file/memory.dart'; -import 'package:test/test.dart'; - -import '../src/datastore/fake_config.dart'; -import '../src/request_handling/request_handler_tester.dart'; - -void main() { - group('StaticFileHandler', () { - late RequestHandlerTester tester; - late FileSystem fs; - - final FakeConfig config = FakeConfig(); - - const String indexFileName = 'index.html'; - const String indexFileContent = 'some html'; - const String dartMapFileName = 'main.dart.js.map'; - const String assetManifestSmcbin = 'AssetManifest.smcbin'; - - setUp(() { - tester = RequestHandlerTester(); - fs = MemoryFileSystem(); - fs.file('build/web/$indexFileName').createSync(recursive: true); - fs.file('build/web/$indexFileName').writeAsStringSync(indexFileContent); - fs.file('build/web/$assetManifestSmcbin').writeAsStringSync(assetManifestSmcbin); - fs.file('build/web/$dartMapFileName').writeAsStringSync('[{}]'); - }); - - Future decodeHandlerBody(Body body) { - return utf8.decoder.bind(body.serialize() as Stream>).first; - } - - test('returns 404 response when file does not exist', () async { - final StaticFileHandler staticFileHandler = StaticFileHandler('i do not exist as a file', config: config, fs: fs); - - expect(tester.get(staticFileHandler), throwsA(const TypeMatcher())); - }); - - test('returns body when file does exist', () async { - final StaticFileHandler staticFileHandler = StaticFileHandler('/$indexFileName', config: config, fs: fs); - - final Body body = await tester.get(staticFileHandler); - expect(body, isNotNull); - final String response = await decodeHandlerBody(body); - expect(response, indexFileContent); - }); - - test('DartMap file does not raise exception', () async { - final StaticFileHandler staticFileHandler = StaticFileHandler('/$dartMapFileName', config: config, fs: fs); - - final Body body = await tester.get(staticFileHandler); - expect(body, isNotNull); - final String response = await decodeHandlerBody(body); - expect(response, '[{}]'); - }); - - test('smcbin file extension is handled correctly', () async { - final StaticFileHandler staticFileHandler = StaticFileHandler('/$assetManifestSmcbin', config: config, fs: fs); - - final Body body = await tester.get(staticFileHandler); - expect(body, isNotNull); - final String response = await decodeHandlerBody(body); - expect(response, assetManifestSmcbin); - }); - - test('No extension files default to plain text', () async { - fs.file('build/web/NOTICE').writeAsStringSync('abc'); - final StaticFileHandler staticFileHandler = StaticFileHandler('/NOTICE', config: config, fs: fs); - - final Body body = await tester.get(staticFileHandler); - expect(body, isNotNull); - final String response = await decodeHandlerBody(body); - expect(response, 'abc'); - }); - }); -} diff --git a/app_dart/test/request_handling/subscription_handler_test.dart b/app_dart/test/request_handling/subscription_handler_test.dart deleted file mode 100644 index ce97bf8b8..000000000 --- a/app_dart/test/request_handling/subscription_handler_test.dart +++ /dev/null @@ -1,171 +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 'dart:io'; -import 'dart:typed_data'; - -import 'package:cocoon_service/src/model/luci/push_message.dart'; -import 'package:cocoon_service/src/request_handling/body.dart'; -import 'package:cocoon_service/src/request_handling/exceptions.dart'; -import 'package:cocoon_service/src/request_handling/subscription_handler.dart'; -import 'package:cocoon_service/src/service/cache_service.dart'; -import 'package:gcloud/service_scope.dart' as ss; -import 'package:test/test.dart'; - -import '../src/datastore/fake_config.dart'; -import '../src/request_handling/fake_authentication.dart'; - -void main() { - group('Subscription', () { - late HttpServer server; - late SubscriptionHandler subscription; - - const PushMessageEnvelope testEnvelope = PushMessageEnvelope( - message: PushMessage( - data: 'test', - messageId: '123', - ), - subscription: 'https://flutter-dashboard.appspot.com/api/luci-status-handler', - ); - - setUp(() async { - server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0); - server.listen((HttpRequest request) { - runZoned( - () { - return ss.fork(() { - return subscription.service(request); - }); - }, - ); - }); - }); - - tearDown(() async { - await server.close(); - }); - - Future issueRequest({String? body}) async { - final HttpClient client = HttpClient(); - final Uri url = Uri(scheme: 'http', host: 'localhost', port: server.port, path: '/path'); - final HttpClientRequest request = await client.getUrl(url); - if (body != null) { - request.contentLength = body.length; - request.write(body); - await request.flush(); - } - return request.close(); - } - - test('failed authentication yields HTTP unauthorized', () async { - subscription = UnauthTest(); - final HttpClientResponse response = await issueRequest(); - expect(response.statusCode, HttpStatus.unauthorized); - expect(await utf8.decoder.bind(response).join(), 'Not authenticated'); - }); - - test('passed authentication yields empty body', () async { - subscription = AuthTest(); - final HttpClientResponse response = await issueRequest(body: jsonEncode(testEnvelope)); - expect(response.statusCode, HttpStatus.ok); - }); - - test('pubsub message is parsed', () async { - subscription = ReadMessageTest(); - final HttpClientResponse response = await issueRequest(body: jsonEncode(testEnvelope)); - expect(response.statusCode, HttpStatus.ok); - final String responseBody = String.fromCharCodes((await response.toList()).first); - expect(responseBody, 'test'); - }); - - test('ensure message ids are idempotent', () async { - final CacheService cache = CacheService(inMemory: true); - subscription = ReadMessageTest(cache); - HttpClientResponse response = await issueRequest(body: jsonEncode(testEnvelope)); - String responseBody = String.fromCharCodes((await response.toList()).first); - expect(response.statusCode, HttpStatus.ok); - // 1. Expected message for this was processed - expect(responseBody, 'test'); - - response = await issueRequest(body: jsonEncode(testEnvelope)); - responseBody = String.fromCharCodes((await response.toList()).first); - expect(response.statusCode, HttpStatus.ok); - // 2. Empty message is returned as this was already processed - expect(responseBody, '123 was already processed'); - expect(await cache.getOrCreate(subscription.subscriptionName, '123', createFn: null), isNotNull); - }); - - test('ensure messages can be retried', () async { - final CacheService cache = CacheService(inMemory: true); - subscription = ErrorTest(cache); - HttpClientResponse response = await issueRequest(body: jsonEncode(testEnvelope)); - Uint8List? messageLock = await cache.getOrCreate('error', '123', createFn: null); - expect(response.statusCode, HttpStatus.internalServerError); - expect(messageLock, isNull); - - response = await issueRequest(body: jsonEncode(testEnvelope)); - messageLock = await cache.getOrCreate('error', '123', createFn: null); - expect(response.statusCode, HttpStatus.internalServerError); - expect(messageLock, isNull); - }); - }); -} - -/// Test stub of [SubscriptionHandler] to validate unauthenticated requests. -class UnauthTest extends SubscriptionHandler { - UnauthTest() - : super( - cache: CacheService(inMemory: true), - config: FakeConfig(), - authProvider: FakeAuthenticationProvider(authenticated: false), - subscriptionName: 'unauth', - ); - - @override - Future get() async => throw StateError('Unreachable'); -} - -/// Test stub of [SubscriptionHandler] to validate authenticated requests. -class AuthTest extends SubscriptionHandler { - AuthTest() - : super( - cache: CacheService(inMemory: true), - config: FakeConfig(), - authProvider: FakeAuthenticationProvider(), - subscriptionName: 'auth', - ); - - @override - Future get() async => Body.empty; -} - -/// Test stub of [SubscriptionHandler] to validate push messages can be read. -class ErrorTest extends SubscriptionHandler { - ErrorTest([CacheService? cache]) - : super( - cache: cache ?? CacheService(inMemory: true), - config: FakeConfig(), - authProvider: FakeAuthenticationProvider(), - subscriptionName: 'error', - ); - - @override - Future get() async => throw const InternalServerError('Test error!'); -} - -/// Test stub of [SubscriptionHandler] to validate push messages can be read. -class ReadMessageTest extends SubscriptionHandler { - ReadMessageTest([CacheService? cache]) - : super( - cache: cache ?? CacheService(inMemory: true), - config: FakeConfig(), - authProvider: FakeAuthenticationProvider(), - subscriptionName: 'read', - ); - - @override - Future get() async => Body.forString(message.data!); -} diff --git a/app_dart/test/request_handling/swarming_authentication_test.dart b/app_dart/test/request_handling/swarming_authentication_test.dart deleted file mode 100644 index 80ce5f833..000000000 --- a/app_dart/test/request_handling/swarming_authentication_test.dart +++ /dev/null @@ -1,106 +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 'package:cocoon_service/src/request_handling/authentication.dart' show AuthenticatedContext; -import 'package:cocoon_service/src/request_handling/exceptions.dart'; -import 'package:cocoon_service/src/request_handling/swarming_authentication.dart'; -import 'package:cocoon_service/src/service/config.dart'; -import 'package:http/http.dart' as http; -import 'package:http/testing.dart'; -import 'package:test/test.dart'; - -import '../src/datastore/fake_config.dart'; -import '../src/request_handling/fake_authentication.dart'; -import '../src/request_handling/fake_http.dart'; - -void main() { - group('SwarmingAuthenticationProvider', () { - late FakeConfig config; - late FakeClientContext clientContext; - late FakeHttpRequest request; - late SwarmingAuthenticationProvider auth; - - setUp(() { - config = FakeConfig(); - clientContext = FakeClientContext(); - request = FakeHttpRequest(); - auth = SwarmingAuthenticationProvider( - config: config, - clientContextProvider: () => clientContext, - ); - }); - - test('fails for App Engine cronjobs', () async { - request.headers.set('X-Appengine-Cron', 'true'); - expect(auth.authenticate(request), throwsA(isA())); - }); - - group('when access token is given', () { - late MockClient httpClient; - - setUp(() { - auth = SwarmingAuthenticationProvider( - config: config, - clientContextProvider: () => clientContext, - httpClientProvider: () => httpClient, - ); - }); - - test('auth succeeds with flutter luci service account', () async { - httpClient = MockClient((_) async => http.Response('{"email": "${Config.luciProdAccount}"}', HttpStatus.ok)); - auth = SwarmingAuthenticationProvider( - config: config, - clientContextProvider: () => clientContext, - httpClientProvider: () => httpClient, - ); - - request.headers.add(SwarmingAuthenticationProvider.kSwarmingTokenHeader, 'token'); - - final AuthenticatedContext result = await auth.authenticate(request); - expect(result.clientContext, same(clientContext)); - }); - - test('auth succeeds with frob service account', () async { - httpClient = MockClient((_) async => http.Response('{"email": "${Config.frobAccount}"}', HttpStatus.ok)); - auth = SwarmingAuthenticationProvider( - config: config, - clientContextProvider: () => clientContext, - httpClientProvider: () => httpClient, - ); - request.headers.add(SwarmingAuthenticationProvider.kSwarmingTokenHeader, 'token'); - - final AuthenticatedContext result = await auth.authenticate(request); - expect(result.clientContext, same(clientContext)); - }); - - test('auth fails with non-luci service account', () async { - httpClient = MockClient((_) async => http.Response('{"email": "abc@gmail.com"}', HttpStatus.ok)); - auth = SwarmingAuthenticationProvider( - config: config, - clientContextProvider: () => clientContext, - httpClientProvider: () => httpClient, - ); - - request.headers.add(SwarmingAuthenticationProvider.kSwarmingTokenHeader, 'unauthenticated token'); - - expect(auth.authenticate(request), throwsA(isA())); - }); - - test('auth fails with unauthenticated service account token', () async { - httpClient = MockClient((_) async => http.Response('Invalid token', HttpStatus.unauthorized)); - auth = SwarmingAuthenticationProvider( - config: config, - clientContextProvider: () => clientContext, - httpClientProvider: () => httpClient, - ); - - request.headers.add(SwarmingAuthenticationProvider.kSwarmingTokenHeader, 'unauthenticated token'); - - expect(auth.authenticate(request), throwsA(isA())); - }); - }); - }); -} diff --git a/app_dart/test/service/bigquery_test.dart b/app_dart/test/service/bigquery_test.dart deleted file mode 100644 index 09f5387d9..000000000 --- a/app_dart/test/service/bigquery_test.dart +++ /dev/null @@ -1,111 +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 'package:cocoon_service/src/service/bigquery.dart'; - -import 'package:googleapis/bigquery/v2.dart'; -import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; - -import '../src/service/fake_bigquery_service.dart'; -import '../src/utilities/mocks.dart'; - -const String semanticsIntegrationTestResponse = ''' -{ - "jobComplete" : true, - "rows": [ - { "f": [ - { "v": "Mac_android android_semantics_integration_test"}, - { "v": "1" }, - { "v": "2" }, - { "v": "101, 102, 103" }, - { "v": "201, 202, 203" }, - { "v": "abc" }, - { "v": "103" }, - { "v": "0.5"}, - {"v": "2023-06-20"}, - {"v": "2023-06-29"} - ] - } - ] -} -'''; - -const String noRecordsResponse = ''' -{ - "jobComplete" : true -} -'''; - -const String jobNotCompleteResponse = ''' -{ - "jobComplete" : false -} -'''; - -const String expectedProjectId = 'project-id'; - -void main() { - late FakeBigqueryService service; - late MockJobsResource jobsResource; - setUp(() { - jobsResource = MockJobsResource(); - service = FakeBigqueryService(jobsResource); - }); - - test('can handle unsuccessful job query', () async { - // When queries flaky data from BigQuery. - when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) { - return Future.value( - QueryResponse.fromJson(jsonDecode(jobNotCompleteResponse) as Map), - ); - }); - bool hasError = false; - try { - await service.listBuilderStatistic(expectedProjectId); - } catch (e) { - expect(e.toString(), 'job does not complete'); - hasError = true; - } - expect(hasError, isTrue); - }); - - test('can handle job query', () async { - // When queries flaky data from BigQuery. - when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) { - return Future.value( - QueryResponse.fromJson(jsonDecode(semanticsIntegrationTestResponse) as Map), - ); - }); - final List statisticList = await service.listBuilderStatistic(expectedProjectId); - expect(statisticList.length, 1); - expect(statisticList[0].name, 'Mac_android android_semantics_integration_test'); - expect(statisticList[0].flakyRate, 0.5); - expect(statisticList[0].succeededBuilds!.length, 3); - expect(statisticList[0].succeededBuilds![0], '203'); - expect(statisticList[0].succeededBuilds![1], '202'); - expect(statisticList[0].succeededBuilds![2], '201'); - expect(statisticList[0].flakyBuilds!.length, 3); - expect(statisticList[0].flakyBuilds![0], '103'); - expect(statisticList[0].flakyBuilds![1], '102'); - expect(statisticList[0].flakyBuilds![2], '101'); - expect(statisticList[0].recentCommit, 'abc'); - expect(statisticList[0].flakyBuildOfRecentCommit, '103'); - expect(statisticList[0].fromDate, '2023-06-20'); - expect(statisticList[0].toDate, '2023-06-29'); - }); - - test('return empty build list when bigquery returns no rows', () async { - when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) { - return Future.value( - QueryResponse.fromJson(jsonDecode(noRecordsResponse) as Map), - ); - }); - final List records = - await service.listRecentBuildRecordsForBuilder(expectedProjectId, builder: 'test', limit: 10); - expect(records.length, 0); - }); -} diff --git a/app_dart/test/service/branch_service_test.dart b/app_dart/test/service/branch_service_test.dart deleted file mode 100644 index 0be792594..000000000 --- a/app_dart/test/service/branch_service_test.dart +++ /dev/null @@ -1,172 +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:cocoon_service/src/model/gerrit/commit.dart'; -import 'package:cocoon_service/src/request_handling/exceptions.dart'; -import 'package:cocoon_service/src/service/branch_service.dart'; -import 'package:cocoon_service/src/service/config.dart'; - -import 'package:github/github.dart' as gh; -import 'package:mockito/mockito.dart'; -import 'package:retry/retry.dart'; -import 'package:test/test.dart'; - -import '../src/datastore/fake_datastore.dart'; -import '../src/service/fake_gerrit_service.dart'; -import '../src/service/fake_github_service.dart'; -import '../src/utilities/entity_generators.dart'; -import '../src/utilities/matchers.dart'; -import '../src/utilities/mocks.mocks.dart'; - -void main() { - late MockConfig config; - late FakeDatastoreDB db; - late BranchService branchService; - late FakeGerritService gerritService; - late FakeGithubService githubService; - late MockRepositoriesService repositories; - - setUp(() { - db = FakeDatastoreDB(); - final MockGitHub github = MockGitHub(); - githubService = FakeGithubService(client: github); - repositories = MockRepositoriesService(); - when(github.repositories).thenReturn(repositories); - config = MockConfig(); - gerritService = FakeGerritService(); - branchService = BranchService( - config: config, - gerritService: gerritService, - retryOptions: const RetryOptions(maxDelay: Duration.zero), - ); - - when(config.createDefaultGitHubService()).thenAnswer((_) async => githubService); - when(config.db).thenReturn(db); - }); - - group('retrieve latest release branches', () { - late MockRepositoriesService mockRepositoriesService; - - setUp(() { - mockRepositoriesService = MockRepositoriesService(); - when(githubService.github.repositories).thenReturn(mockRepositoriesService); - }); - - test('return beta, stable, and latest candidate branches', () async { - final gh.Branch stableBranch = generateBranch(1, name: 'flutter-2.13-candidate.0', sha: '123stable'); - final gh.Branch betaBranch = generateBranch(2, name: 'flutter-3.2-candidate.5', sha: '456beta'); - final gh.Branch candidateBranch = generateBranch(3, name: 'flutter-3.4-candidate.5', sha: '789dev'); - final gh.Branch candidateBranchOne = generateBranch(4, name: 'flutter-3.3-candidate.9', sha: 'lagerZValue'); - final gh.Branch candidateBranchTwo = - generateBranch(5, name: 'flutter-2.15-candidate.99', sha: 'superLargeYZvalue'); - final gh.Branch candidateBranchThree = generateBranch(6, name: 'flutter-0.5-candidate.0', sha: 'someZeroValues'); - final gh.Branch candidateCherrypickBranch = - generateBranch(6, name: 'cherry-picks-flutter-3.11-candidate.3', sha: 'bad'); - - when(mockRepositoriesService.listBranches(any)).thenAnswer((Invocation invocation) { - return Stream.fromIterable([ - candidateBranch, - candidateBranchOne, - stableBranch, - betaBranch, - candidateBranchTwo, - candidateBranchThree, - candidateCherrypickBranch, - ]); - }); - final List> result = - await branchService.getReleaseBranches(githubService: githubService, slug: Config.flutterSlug); - expect(result.length, 4); - expect(result[1]['branch'], 'flutter-2.13-candidate.0'); - expect(result[1]['name'], 'stable'); - final devBranch = result.singleWhere((Map branch) => branch['name'] == 'dev'); - expect(devBranch['branch'], 'flutter-3.4-candidate.5'); - }); - }); - - group('branchFlutterRecipes', () { - const String branch = 'flutter-2.13-candidate.0'; - const String sha = 'abc123'; - setUp(() { - gerritService.branchesValue = []; - when(repositories.getCommit(Config.engineSlug, sha)).thenAnswer((_) async => generateGitCommit(5)); - }); - - test('does not create branch that already exists', () async { - gerritService.branchesValue = [branch]; - expect( - () async => branchService.branchFlutterRecipes(branch, sha), - throwsExceptionWith('$branch already exists'), - ); - }); - - test('throws BadRequest if github commit has no branch time', () async { - gerritService.commitsValue = []; - when(repositories.getCommit(Config.engineSlug, sha)).thenAnswer( - (_) async => gh.RepositoryCommit( - commit: gh.GitCommit( - committer: gh.GitCommitUser( - 'dash', - 'dash@flutter.dev', - null, - ), - ), - ), - ); - expect( - () async => branchService.branchFlutterRecipes(branch, sha), - throwsExceptionWith('$sha has no commit time'), - ); - }); - - test('does not create branch if a good branch point cannot be found', () async { - gerritService.commitsValue = []; - when(repositories.getCommit(Config.engineSlug, sha)).thenAnswer( - (_) async => generateGitCommit(5), - ); - expect( - () async => branchService.branchFlutterRecipes(branch, sha), - throwsExceptionWith( - 'HTTP 500: Failed to find a revision to flutter/recipes for $branch before 1969-12-31', - ), - ); - }); - - test('creates branch', () async { - await branchService.branchFlutterRecipes(branch, sha); - }); - - test('creates branch when GitHub requires retries', () async { - int attempts = 0; - when(repositories.getCommit(Config.engineSlug, sha)).thenAnswer((_) async { - attempts++; - if (attempts == 3) { - return generateGitCommit(5); - } - throw gh.GitHubError(MockGitHub(), 'Failed to get commit'); - }); - await branchService.branchFlutterRecipes(branch, sha); - }); - - test('ensure createDefaultGithubService is called once for each retry', () async { - int attempts = 0; - when(repositories.getCommit(Config.engineSlug, sha)).thenAnswer((_) async { - attempts++; - if (attempts == 3) { - return generateGitCommit(5); - } - throw gh.GitHubError(MockGitHub(), 'Failed to get commit'); - }); - await branchService.branchFlutterRecipes(branch, sha); - - verify(config.createDefaultGitHubService()).called(attempts); - }); - - test('creates branch when there is a similar branch', () async { - gerritService.branchesValue = ['$branch-similar']; - - await branchService.branchFlutterRecipes(branch, sha); - }); - }); -} diff --git a/app_dart/test/service/build_status_provider_test.dart b/app_dart/test/service/build_status_provider_test.dart deleted file mode 100644 index d241e4705..000000000 --- a/app_dart/test/service/build_status_provider_test.dart +++ /dev/null @@ -1,249 +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/model/appengine/commit.dart'; -import 'package:cocoon_service/src/model/appengine/task.dart'; -import 'package:cocoon_service/src/service/build_status_provider.dart'; -import 'package:cocoon_service/src/service/datastore.dart'; -import 'package:gcloud/db.dart'; -import 'package:github/github.dart'; -import 'package:test/test.dart'; - -import '../src/datastore/fake_config.dart'; -import '../src/datastore/fake_datastore.dart'; -import '../src/utilities/entity_generators.dart'; - -List oneCommit = [ - Commit( - key: Key.emptyKey(Partition('ns')).append(Commit, id: 'sha1'), - repository: 'flutter/flutter', - sha: 'sha1', - ), -]; - -List twoCommits = [ - Commit( - key: Key.emptyKey(Partition('ns')).append(Commit, id: 'sha1'), - repository: 'flutter/flutter', - sha: 'sha1', - ), - Commit( - key: Key.emptyKey(Partition('ns')).append(Commit, id: 'sha2'), - repository: 'flutter/flutter', - sha: 'sha2', - ), -]; - -List allGreen = [ - generateTask(1, status: Task.statusSucceeded), - generateTask(2, status: Task.statusSucceeded), - generateTask(3, status: Task.statusSucceeded), -]; - -List allRed = [ - generateTask(1, status: Task.statusFailed), - generateTask(2, status: Task.statusFailed), - generateTask(3, status: Task.statusFailed), -]; - -List middleTaskFailed = [ - generateTask(1, status: Task.statusSucceeded), - generateTask(2, status: Task.statusFailed), - generateTask(3, status: Task.statusSucceeded), -]; - -List middleTaskCanceled = [ - generateTask(1, status: Task.statusSucceeded), - generateTask(2, status: Task.statusCancelled), - generateTask(3, status: Task.statusSucceeded), -]; - -List middleTaskFlakyFailed = [ - generateTask(1, status: Task.statusSucceeded), - generateTask(2, status: Task.statusFailed, isFlaky: true), - generateTask(3, status: Task.statusSucceeded), -]; - -List middleTaskInProgress = [ - generateTask(1, status: Task.statusSucceeded), - generateTask(2, status: Task.statusInProgress), - generateTask(3, status: Task.statusSucceeded), -]; - -List middleTaskRerunning = [ - generateTask(1, status: Task.statusSucceeded), - generateTask(2, status: Task.statusNew, attempts: 2), - generateTask(3, status: Task.statusSucceeded), -]; - -List middleTaskRerunGreen = [ - generateTask(1, status: Task.statusSucceeded), - generateTask(2, status: Task.statusSucceeded, attempts: 2), - generateTask(3, status: Task.statusSucceeded), -]; - -List middleTaskInfraFailure = [ - generateTask(1, status: Task.statusSucceeded), - generateTask(2, status: Task.statusInfraFailure), - generateTask(3, status: Task.statusSucceeded), -]; - -void main() { - group('BuildStatusProvider', () { - late FakeDatastoreDB db; - late BuildStatusService buildStatusService; - FakeConfig config; - DatastoreService datastoreService; - - final RepositorySlug slug = RepositorySlug('flutter', 'flutter'); - - setUp(() { - db = FakeDatastoreDB(); - config = FakeConfig(dbValue: db); - datastoreService = DatastoreService(config.db, 5); - buildStatusService = BuildStatusService.defaultProvider(datastoreService); - }); - - group('calculateStatus', () { - test('returns failure if there are no commits', () async { - final BuildStatus? status = await buildStatusService.calculateCumulativeStatus(slug); - expect(status, BuildStatus.failure(const [])); - }); - - test('returns success if top commit is all green', () async { - db.addOnQuery((Iterable results) => oneCommit); - db.addOnQuery((Iterable results) => allGreen); - final BuildStatus? status = await buildStatusService.calculateCumulativeStatus(slug); - expect(status, BuildStatus.success()); - }); - - test('returns success if top commit is all green followed by red commit', () async { - db.addOnQuery((Iterable results) => twoCommits); - int row = 0; - db.addOnQuery((Iterable results) { - return row++ == 0 ? allGreen : middleTaskFailed; - }); - final BuildStatus? status = await buildStatusService.calculateCumulativeStatus(slug); - expect(status, BuildStatus.success()); - }); - - test('returns failure if last commit contains any red tasks', () async { - db.addOnQuery((Iterable results) => oneCommit); - db.addOnQuery((Iterable results) => middleTaskFailed); - final BuildStatus? status = await buildStatusService.calculateCumulativeStatus(slug); - expect(status, BuildStatus.failure(const ['task2'])); - }); - - test('returns failure if last commit contains any canceled tasks', () async { - db.addOnQuery((Iterable results) => oneCommit); - db.addOnQuery((Iterable results) => middleTaskCanceled); - final BuildStatus? status = await buildStatusService.calculateCumulativeStatus(slug); - expect(status, BuildStatus.failure(const ['task2'])); - }); - - test('ensure failed task do not have duplicates when last consecutive commits contains red tasks', () async { - db.addOnQuery((Iterable results) => twoCommits); - db.addOnQuery((Iterable results) => middleTaskFailed); - - final BuildStatus? status = await buildStatusService.calculateCumulativeStatus(slug); - expect(status, BuildStatus.failure(const ['task2'])); - }); - - test('ignores failures on flaky commits', () async { - db.addOnQuery((Iterable results) => oneCommit); - db.addOnQuery((Iterable results) => middleTaskFlakyFailed); - final BuildStatus? status = await buildStatusService.calculateCumulativeStatus(slug); - expect(status, BuildStatus.success()); - }); - - test('returns success if partial green, and all unfinished tasks were last green', () async { - db.addOnQuery((Iterable results) => twoCommits); - int row = 0; - db.addOnQuery((Iterable results) { - return row++ == 0 ? middleTaskInProgress : allGreen; - }); - final BuildStatus? status = await buildStatusService.calculateCumulativeStatus(slug); - expect(status, BuildStatus.success()); - }); - - test('returns failure if partial green, and any unfinished task was last red', () async { - db.addOnQuery((Iterable results) => twoCommits); - int row = 0; - db.addOnQuery((Iterable results) { - return row++ == 0 ? middleTaskInProgress : middleTaskFailed; - }); - final BuildStatus? status = await buildStatusService.calculateCumulativeStatus(slug); - expect(status, BuildStatus.failure(const ['task2'])); - }); - - test('returns failure when green but a task is rerunning', () async { - db.addOnQuery((Iterable results) => twoCommits); - int row = 0; - db.addOnQuery((Iterable results) { - return row++ == 0 ? middleTaskRerunning : allGreen; - }); - final BuildStatus? status = await buildStatusService.calculateCumulativeStatus(slug); - expect(status, BuildStatus.failure(const ['task2'])); - }); - - test('returns failure when a task has an infra failure', () async { - db.addOnQuery((Iterable results) => twoCommits); - int row = 0; - db.addOnQuery((Iterable results) { - return row++ == 0 ? middleTaskInfraFailure : allGreen; - }); - final BuildStatus? status = await buildStatusService.calculateCumulativeStatus(slug); - expect(status, BuildStatus.failure(const ['task2'])); - }); - - test('returns success when all green with a successful rerun', () async { - db.addOnQuery((Iterable results) => twoCommits); - int row = 0; - db.addOnQuery((Iterable results) { - return row++ == 0 ? middleTaskRerunGreen : allRed; - }); - final BuildStatus? status = await buildStatusService.calculateCumulativeStatus(slug); - expect(status, BuildStatus.success()); - }); - - test('return status when with branch parameter', () async { - final Commit commit1 = generateCommit(1, branch: 'flutter-0.0-candidate.0'); - final Commit commit2 = generateCommit(2, branch: 'master'); - db.values[commit1.key] = commit1; - db.values[commit2.key] = commit2; - - final Task task1 = Task( - key: commit1.key.append(Task, id: 1), - commitKey: commit1.key, - name: 'task1', - status: Task.statusSucceeded, - stageName: 'stage1', - ); - - db.values[task1.key] = task1; - - // Test master branch. - final List statuses1 = await buildStatusService - .retrieveCommitStatus( - limit: 5, - slug: slug, - ) - .toList(); - expect(statuses1.length, 1); - expect(statuses1.first.commit.branch, 'master'); - - // Test dev branch. - final List statuses2 = await buildStatusService - .retrieveCommitStatus( - limit: 5, - branch: 'flutter-0.0-candidate.0', - slug: slug, - ) - .toList(); - expect(statuses2.length, 1); - expect(statuses2.first.commit.branch, 'flutter-0.0-candidate.0'); - }); - }); - }); -} diff --git a/app_dart/test/service/buildbucket_test.dart b/app_dart/test/service/buildbucket_test.dart deleted file mode 100644 index 9e3c1defe..000000000 --- a/app_dart/test/service/buildbucket_test.dart +++ /dev/null @@ -1,377 +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 'dart:io'; - -import 'package:cocoon_service/src/model/luci/buildbucket.dart'; -import 'package:cocoon_service/src/request_handling/body.dart'; -import 'package:cocoon_service/src/service/buildbucket.dart'; -import 'package:googleapis_auth/auth_io.dart'; -import 'package:http/http.dart' as http; -import 'package:http/testing.dart'; -import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; - -import '../src/utilities/mocks.dart'; - -void main() { - group('BatchResponse tests', () { - test('fromJson returns an empty list', () { - const String jsonString = '{"responses":[{"searchBuilds":{}},{"searchBuilds":{}}]}'; - final Map map = json.decode(jsonString) as Map; - final BatchResponse response = BatchResponse.fromJson(map); - expect(response, isNotNull); - expect(response.responses, isNotNull); - }); - }); - - group('Client tests', () { - late MockClient httpClient; - late MockAccessTokenService mockAccessTokenProvider; - - const BuilderId builderId = BuilderId( - bucket: 'prod', - builder: 'Linux', - project: 'flutter', - ); - - setUp(() { - httpClient = MockClient((_) => throw Exception('Client not defined')); - mockAccessTokenProvider = MockAccessTokenService(); - }); - - Future httpTest( - R request, - String response, - String urlPrefix, - String expectedPath, - Future Function(BuildBucketClient) requestCallback, - ) async { - when(mockAccessTokenProvider.createAccessToken()).thenAnswer((_) async { - return AccessToken('Bearer', 'data', DateTime.utc(2119)); - }); - httpClient = MockClient((http.Request request) async { - expect(request.headers['content-type'], 'application/json; charset=utf-8'); - expect(request.headers['accept'], 'application/json'); - expect(request.headers['authorization'], 'Bearer data'); - if (request.method == 'POST' && request.url.toString() == 'https://localhost/$urlPrefix/$expectedPath') { - return http.Response(response, HttpStatus.accepted); - } - return http.Response('Test exception: A mock response was not returned', HttpStatus.internalServerError); - }); - final BuildBucketClient client = BuildBucketClient( - httpClient: httpClient, - accessTokenService: mockAccessTokenProvider, - ); - final T result = await requestCallback(client); - return result; - } - - test('Throws the right exception', () async { - when(mockAccessTokenProvider.createAccessToken()).thenAnswer((_) async { - return AccessToken('Bearer', 'data', DateTime.utc(2119)); - }); - httpClient = MockClient((_) async => http.Response('Error', HttpStatus.forbidden)); - final BuildBucketClient client = BuildBucketClient( - buildBucketBuildUri: 'https://localhost', - httpClient: httpClient, - accessTokenService: mockAccessTokenProvider, - ); - try { - await client.batch(const BatchRequest()); - } on BuildBucketException catch (ex) { - expect(ex.statusCode, HttpStatus.forbidden); - expect(ex.message, 'Error'); - return; - } - fail('Did not throw expected exception'); - }); - - test('ScheduleBuild', () async { - const ScheduleBuildRequest request = ScheduleBuildRequest( - builderId: builderId, - experimental: Trinary.yes, - tags: >{ - 'user_agent': ['flutter_cocoon'], - 'flutter_pr': ['true', '1'], - 'cipd_version': ['/refs/heads/main'], - }, - properties: { - 'git_url': 'https://github.com/flutter/flutter', - 'git_ref': 'refs/pull/1/head', - }, - ); - - final Build build = await httpTest( - request, - buildJson, - 'builds', - 'ScheduleBuild', - (BuildBucketClient client) => client.scheduleBuild(request, buildBucketUri: 'https://localhost/builds'), - ); - - expect(build.id, '123'); - expect(build.tags!.length, 3); - expect(build.tags, >{ - 'user_agent': ['flutter_cocoon'], - 'flutter_pr': ['1'], - 'cipd_version': ['/refs/heads/main'], - }); - }); - - test('CancelBuild', () async { - const CancelBuildRequest request = CancelBuildRequest( - id: '1234', - summaryMarkdown: 'Because I felt like it.', - ); - - final Build build = await httpTest( - request, - buildJson, - 'builds', - 'CancelBuild', - (BuildBucketClient client) => client.cancelBuild(request, buildBucketUri: 'https://localhost/builds'), - ); - - expect(build.id, '123'); - expect(build.tags!.length, 3); - }); - - test('BatchBuildRequest', () async { - const BatchRequest request = BatchRequest( - requests: [ - Request( - scheduleBuild: ScheduleBuildRequest( - builderId: builderId, - experimental: Trinary.yes, - tags: >{ - 'user_agent': ['flutter_cocoon'], - 'flutter_pr': ['true', '1'], - 'cipd_version': ['/refs/heads/main'], - }, - properties: { - 'git_url': 'https://github.com/flutter/flutter', - 'git_ref': 'refs/pull/1/head', - }, - ), - ), - ], - ); - - final BatchResponse response = await httpTest( - request, - batchJson, - 'builds', - 'Batch', - (BuildBucketClient client) => client.batch(request, buildBucketUri: 'https://localhost/builds'), - ); - expect(response.responses!.length, 1); - expect(response.responses!.first.getBuild!.status, Status.success); - expect(response.responses!.first.getBuild!.tags, >{ - 'user_agent': ['flutter_cocoon'], - 'flutter_pr': ['1'], - 'cipd_version': ['/refs/heads/main'], - }); - }); - - test('Batch', () async { - const BatchRequest request = BatchRequest( - requests: [ - Request( - getBuild: GetBuildRequest( - builderId: builderId, - buildNumber: 123, - ), - ), - ], - ); - - final BatchResponse response = await httpTest( - request, - batchJson, - 'builds', - 'Batch', - (BuildBucketClient client) => client.batch(request, buildBucketUri: 'https://localhost/builds'), - ); - - expect(response.responses!.length, 1); - expect(response.responses!.first.getBuild!.status, Status.success); - }); - - test('GetBuild', () async { - const GetBuildRequest request = GetBuildRequest( - id: '1234', - ); - - final Build build = await httpTest( - request, - buildJson, - 'builds', - 'GetBuild', - (BuildBucketClient client) => client.getBuild(request, buildBucketUri: 'https://localhost/builds'), - ); - - expect(build.id, '123'); - expect(build.tags!.length, 3); - }); - - test('SearchBuilds', () async { - const SearchBuildsRequest request = SearchBuildsRequest( - predicate: BuildPredicate( - tags: >{ - 'flutter_pr': ['1'], - }, - ), - ); - - final SearchBuildsResponse response = await httpTest( - request, - searchJson, - 'builds', - 'SearchBuilds', - (BuildBucketClient client) => client.searchBuilds(request, buildBucketUri: 'https://localhost/builds'), - ); - - expect(response.builds!.length, 1); - expect(response.builds!.first.number, 9151); - }); - - test('ListBuilders', () async { - const ListBuildersRequest request = ListBuildersRequest(project: 'test'); - - final ListBuildersResponse listBuildersResponse = await httpTest( - request, - builderJson, - 'builders', - 'ListBuilders', - (BuildBucketClient client) => client.listBuilders(request, buildBucketUri: 'https://localhost/builders'), - ); - - expect(listBuildersResponse.builders!.length, 2); - expect(listBuildersResponse.builders!.map((e) => e.id!.builder!).toList(), ['Linux test', 'Mac test']); - }); - }); -} - -const String builderJson = '''${BuildBucketClient.kRpcResponseGarbage} -{ - "builders": [{ - "id": { - "project": "flutter", - "bucket": "prod", - "builder": "Linux test" - } - }, { - "id": { - "project": "flutter", - "bucket": "prod", - "builder": "Mac test" - } - }] -}'''; - -const String searchJson = '''${BuildBucketClient.kRpcResponseGarbage} -{ - "builds": [ - { - "status": "SUCCESS", - "updateTime": "2019-07-26T20:43:52.875240Z", - "createdBy": "project:flutter", - "builder": { - "project": "flutter", - "builder": "Linux", - "bucket": "prod" - }, - "number": 9151, - "id": "8906840690092270320", - "startTime": "2019-07-26T20:10:22.271996Z", - "input": { - "gitilesCommit": { - "project": "external/github.com/flutter/flutter", - "host": "chromium.googlesource.com", - "ref": "refs/heads/master", - "id": "3068fc4f7c78599ab4a09b096f0672e8510fc7e6" - } - }, - "endTime": "2019-07-26T20:43:52.341494Z", - "createTime": "2019-07-26T20:10:15.744632Z" - } - ] -}'''; -const String batchJson = '''${BuildBucketClient.kRpcResponseGarbage} -{ - "responses": [ - { - "getBuild": { - "status": "SUCCESS", - "updateTime": "2019-07-15T23:20:56.930928Z", - "createdBy": "project:flutter", - "builder": { - "project": "flutter", - "builder": "Linux", - "bucket": "prod" - }, - "number": 9000, - "id": "8907827286280251904", - "startTime": "2019-07-15T22:49:06.222424Z", - "input": { - "gitilesCommit": { - "project": "external/github.com/flutter/flutter", - "host": "chromium.googlesource.com", - "ref": "refs/heads/master", - "id": "6b17840cbf1a7d7b6208117226ded21a5ec0d55c" - } - }, - "endTime": "2019-07-15T23:20:32.610402Z", - "createTime": "2019-07-15T22:48:44.299749Z", - "tags": [ - { - "key": "user_agent", - "value": "flutter_cocoon" - }, - { - "key": "flutter_pr", - "value": "1" - }, - { - "key": "cipd_version", - "value": "/refs/heads/main" - } - ] - } - } - ] -}'''; - -const String buildJson = '''${BuildBucketClient.kRpcResponseGarbage} -{ - "id": "123", - "builder": { - "project": "flutter", - "bucket": "prod", - "builder": "Linux" - }, - "number": 321, - "createdBy": "cocoon@cocoon", - "canceledBy": null, - "startTime": "2019-08-01T11:00:00", - "endTime": null, - "status": "SCHEDULED", - "input": { - "experimental": true - }, - "tags": [{ - "key": "user_agent", - "value": "flutter_cocoon" - }, { - "key": "flutter_pr", - "value": "1" - }, - { - "key": "cipd_version", - "value": "/refs/heads/main" - }] -}'''; diff --git a/app_dart/test/service/cache_service_test.dart b/app_dart/test/service/cache_service_test.dart deleted file mode 100644 index d2d331065..000000000 --- a/app_dart/test/service/cache_service_test.dart +++ /dev/null @@ -1,269 +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:typed_data'; - -import 'package:cocoon_service/src/service/cache_service.dart'; -import 'package:mockito/mockito.dart'; -import 'package:neat_cache/neat_cache.dart'; -import 'package:test/test.dart'; - -import '../src/utilities/mocks.dart'; - -void main() { - group('CacheService', () { - late CacheService cache; - - const String testSubcacheName = 'test'; - - setUp(() { - cache = CacheService(inMemory: true, inMemoryMaxNumberEntries: 1); - }); - - test('returns null when no value exists', () async { - final Uint8List? value = await cache.getOrCreate( - testSubcacheName, - 'abc', - createFn: null, - ); - - expect(value, isNull); - }); - - test('returns value when it exists', () async { - const String testKey = 'abc'; - final Uint8List expectedValue = Uint8List.fromList('123'.codeUnits); - - await cache.set(testSubcacheName, testKey, expectedValue); - - final Uint8List? value = await cache.getOrCreate( - testSubcacheName, - testKey, - createFn: null, - ); - - expect(value, expectedValue); - }); - - test('last used value is rotated out of cache if cache is full', () async { - const String testKey1 = 'abc'; - const String testKey2 = 'def'; - final Uint8List expectedValue1 = Uint8List.fromList('123'.codeUnits); - final Uint8List expectedValue2 = Uint8List.fromList('456'.codeUnits); - - await cache.set(testSubcacheName, testKey1, expectedValue1); - await cache.set(testSubcacheName, testKey2, expectedValue2); - - final Uint8List? value1 = await cache.getOrCreate( - testSubcacheName, - testKey1, - createFn: null, - ); - expect(value1, null); - - final Uint8List? value2 = await cache.getOrCreate( - testSubcacheName, - testKey2, - createFn: null, - ); - expect(value2, expectedValue2); - }); - - test('retries when get throws exception', () async { - final Cache mockMainCache = MockCache(); - final Cache mockTestSubcache = MockCache(); - when>(mockMainCache.withPrefix(testSubcacheName)).thenReturn(mockTestSubcache); - - int getCallCount = 0; - final Entry entry = FakeEntry(); - // Only on the first call do we want it to throw the exception. - when(mockTestSubcache['does not matter']).thenAnswer( - (Invocation invocation) => getCallCount++ < 1 ? throw Exception('simulate stream sink error') : entry, - ); - - cache.cacheValue = mockMainCache; - - final Uint8List? value = await cache.getOrCreate( - testSubcacheName, - 'does not matter', - createFn: null, - ); - verify(mockTestSubcache['does not matter']).called(2); - expect(value, Uint8List.fromList('abc123'.codeUnits)); - }); - - test('returns null if reaches max attempts of retries', () async { - final Cache mockMainCache = MockCache(); - final Cache mockTestSubcache = MockCache(); - when>(mockMainCache.withPrefix(testSubcacheName)).thenReturn(mockTestSubcache); - - int getCallCount = 0; - final Entry entry = FakeEntry(); - // Always throw exception until max retries - when(mockTestSubcache['does not matter']).thenAnswer( - (Invocation invocation) => - getCallCount++ < CacheService.maxCacheGetAttempts ? throw Exception('simulate stream sink error') : entry, - ); - - cache.cacheValue = mockMainCache; - - final Uint8List? value = await cache.getOrCreate( - testSubcacheName, - 'does not matter', - createFn: null, - ); - verify(mockTestSubcache['does not matter']).called(CacheService.maxCacheGetAttempts); - expect(value, isNull); - }); - - test('creates value if given createFn', () async { - final Uint8List cat = Uint8List.fromList('cat'.codeUnits); - Future createCat() async => cat; - - final Uint8List? value = await cache.getOrCreate(testSubcacheName, 'dog', createFn: createCat); - - expect(value, cat); - }); - - test('purge removes value', () async { - const String testKey = 'abc'; - final Uint8List expectedValue = Uint8List.fromList('123'.codeUnits); - - await cache.set(testSubcacheName, testKey, expectedValue); - - final Uint8List? value = await cache.getOrCreate( - testSubcacheName, - testKey, - createFn: null, - ); - - expect(value, expectedValue); - - await cache.purge(testSubcacheName, testKey); - - final Uint8List? valueAfterPurge = await cache.getOrCreate( - testSubcacheName, - testKey, - createFn: null, - ); - expect(valueAfterPurge, isNull); - }); - - test('sets ttl from set', () async { - final Cache mockMainCache = MockCache(); - final Cache mockTestSubcache = MockCache(); - when>(mockMainCache.withPrefix(testSubcacheName)).thenReturn(mockTestSubcache); - - final Entry entry = MockFakeEntry(); - when(mockTestSubcache['fish']).thenAnswer((Invocation invocation) => entry); - cache.cacheValue = mockMainCache; - - const Duration testDuration = Duration(days: 40); - when(entry.set(any, testDuration)).thenAnswer((_) async => null); - verifyNever(entry.set(any, testDuration)); - await cache.set(testSubcacheName, 'fish', Uint8List.fromList('bigger fish'.codeUnits), ttl: testDuration); - verify(entry.set(any, testDuration)).called(1); - }); - - test('sets ttl is passed through correctly from createFn', () async { - const String value = 'bigger fish'; - final Uint8List valueBytes = Uint8List.fromList(value.codeUnits); - const Duration testDuration = Duration(days: 40); - - final Entry entry = MockFakeEntry(); - when(entry.set(valueBytes, testDuration)).thenAnswer((_) async => valueBytes); - - final Cache mockTestSubcache = MockCache(); - final Cache mockMainCache = MockCache(); - when>(mockMainCache.withPrefix(testSubcacheName)).thenReturn(mockTestSubcache); - when(mockTestSubcache['fish']).thenAnswer((Invocation invocation) => entry); - cache.cacheValue = mockMainCache; - - verifyNever(entry.set(any, testDuration)); - await cache.getOrCreate( - testSubcacheName, - 'fish', - createFn: () async => valueBytes, - ttl: testDuration, - ); - verify(entry.set(any, testDuration)).called(1); - }); - - test('set does not block read attempt', () async { - const String testKey = 'abc'; - final Uint8List expectedValue = Uint8List.fromList('123'.codeUnits); - - final cacheWrite = cache.setWithLocking(testSubcacheName, testKey, expectedValue); - Uint8List? valueAfterSet = await cache.getOrCreateWithLocking( - testSubcacheName, - testKey, - createFn: null, - ); - - expect(valueAfterSet, null); - await cacheWrite; - valueAfterSet = await cache.getOrCreateWithLocking( - testSubcacheName, - testKey, - createFn: null, - ); - expect(valueAfterSet, expectedValue); - }); - - test('read locks are not blocking', () async { - const String testKey = 'abc'; - final Uint8List expectedValue = Uint8List.fromList('123'.codeUnits); - - await cache.setWithLocking(testSubcacheName, testKey, expectedValue); - final Future valueAfterSet = cache.getOrCreateWithLocking( - testSubcacheName, - testKey, - createFn: null, - ); - final Uint8List? valueAfterSet2 = await cache.getOrCreateWithLocking( - testSubcacheName, - testKey, - createFn: null, - ); - - expect(valueAfterSet2, expectedValue); - await valueAfterSet.then((value) => expect(value, expectedValue)); - }); - - test('write locks are blocking', () async { - const String testKey = 'abc'; - final Uint8List expectedValue = Uint8List.fromList('123'.codeUnits); - final Uint8List newValue = Uint8List.fromList('345'.codeUnits); - - final cacheWrite = cache.setWithLocking(testSubcacheName, testKey, expectedValue); - final cacheWrite2 = cache.setWithLocking(testSubcacheName, testKey, newValue); - await cacheWrite; - final Uint8List? readValue = await cache.getOrCreateWithLocking( - testSubcacheName, - testKey, - createFn: null, - ); - expect(readValue, expectedValue); - await cacheWrite2; - }); - }); -} - -class FakeEntry extends Entry { - Uint8List value = Uint8List.fromList('abc123'.codeUnits); - - @override - Future get([Future Function()? create, Duration? ttl]) async => value; - - @override - Future purge({int retries = 0}) => throw UnimplementedError(); - - @override - Future set(Uint8List? value, [Duration? ttl]) async { - value = value; - - return value; - } -} diff --git a/app_dart/test/service/commit_service_test.dart b/app_dart/test/service/commit_service_test.dart deleted file mode 100644 index 5a88ac60d..000000000 --- a/app_dart/test/service/commit_service_test.dart +++ /dev/null @@ -1,184 +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:cocoon_service/src/model/appengine/commit.dart'; -import 'package:cocoon_service/src/service/commit_service.dart'; -import 'package:github/github.dart'; - -import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; -import 'package:github/hooks.dart'; - -import '../src/datastore/fake_datastore.dart'; -import '../src/utilities/entity_generators.dart'; -import '../src/utilities/mocks.mocks.dart'; -import '../src/utilities/webhook_generators.dart'; - -void main() { - late MockConfig config; - late FakeDatastoreDB db; - late CommitService commitService; - late MockGithubService githubService; - late MockRepositoriesService repositories; - late MockGitHub github; - const String owner = 'flutter'; - const String repository = 'flutter'; - const String branch = 'coolest-branch'; - const String sha = '1234'; - const String message = 'Adding null safety'; - const String avatarUrl = 'https://avatars.githubusercontent.com/u/fake-user-num'; - const String username = 'AwesomeGithubUser'; - const String dateTimeAsString = '2023-08-18T19:27:00Z'; - - setUp(() { - db = FakeDatastoreDB(); - github = MockGitHub(); - githubService = MockGithubService(); - when(githubService.github).thenReturn(github); - repositories = MockRepositoriesService(); - when(github.repositories).thenReturn(repositories); - config = MockConfig(); - commitService = CommitService(config: config); - - when(config.createDefaultGitHubService()).thenAnswer((_) async => githubService); - when(config.db).thenReturn(db); - }); - - group('handleCreateGithubRequest', () { - test('adds commit to db if it does not exist in the datastore', () async { - expect(db.values.values.whereType().length, 0); - - when(githubService.getReference(RepositorySlug(owner, repository), 'heads/$branch')) - .thenAnswer((Invocation invocation) { - return Future.value( - GitReference(ref: 'refs/$branch', object: GitObject('', sha, '')), - ); - }); - - when(repositories.getCommit(RepositorySlug(owner, repository), sha)).thenAnswer((Invocation invocation) { - return Future.value( - RepositoryCommit( - sha: sha, - author: User( - login: username, - avatarUrl: avatarUrl, - ), - commit: GitCommit(message: message), - ), - ); - }); - - final CreateEvent createEvent = generateCreateBranchEvent(branch, '$owner/$repository'); - await commitService.handleCreateGithubRequest(createEvent); - - expect(db.values.values.whereType().length, 1); - final Commit commit = db.values.values.whereType().single; - expect(commit.repository, '$owner/$repository'); - expect(commit.message, message); - expect(commit.key.id, '$owner/$repository/$branch/$sha'); - expect(commit.sha, sha); - expect(commit.author, username); - expect(commit.authorAvatarUrl, avatarUrl); - expect(commit.branch, branch); - }); - - test('does not add commit to db if it exists in the datastore', () async { - final Commit existingCommit = generateCommit( - 1, - sha: sha, - branch: branch, - owner: owner, - repo: repository, - timestamp: 0, - ); - final List datastoreCommit = [existingCommit]; - await config.db.commit(inserts: datastoreCommit); - expect(db.values.values.whereType().length, 1); - - when(githubService.getReference(RepositorySlug(owner, repository), 'heads/$branch')) - .thenAnswer((Invocation invocation) { - return Future.value( - GitReference(ref: 'refs/$branch', object: GitObject('', sha, '')), - ); - }); - - when(repositories.getCommit(RepositorySlug(owner, repository), sha)).thenAnswer((Invocation invocation) { - return Future.value( - RepositoryCommit( - sha: sha, - author: User( - createdAt: DateTime.parse(dateTimeAsString), - login: username, - avatarUrl: avatarUrl, - ), - commit: GitCommit(message: message), - ), - ); - }); - - final CreateEvent createEvent = generateCreateBranchEvent(branch, '$owner/$repository'); - await commitService.handleCreateGithubRequest(createEvent); - - expect(db.values.values.whereType().length, 1); - final Commit commit = db.values.values.whereType().single; - expect(commit, existingCommit); - }); - }); - - group('handlePushGithubRequest', () { - test('adds commit to db if it does not exist in the datastore', () async { - expect(db.values.values.whereType().length, 0); - - final Map pushEvent = generatePushEvent( - branch, - owner, - repository, - message: message, - sha: sha, - avatarUrl: avatarUrl, - username: username, - ); - await commitService.handlePushGithubRequest(pushEvent); - - expect(db.values.values.whereType().length, 1); - final Commit commit = db.values.values.whereType().single; - expect(commit.repository, '$owner/$repository'); - expect(commit.message, message); - expect(commit.key.id, '$owner/$repository/$branch/$sha'); - expect(commit.sha, sha); - expect(commit.author, username); - expect(commit.authorAvatarUrl, avatarUrl); - expect(commit.branch, branch); - }); - - test('does not add commit to db if it exists in the datastore', () async { - final Commit existingCommit = generateCommit( - 1, - sha: sha, - branch: branch, - owner: owner, - repo: repository, - timestamp: 0, - ); - final List datastoreCommit = [existingCommit]; - await config.db.commit(inserts: datastoreCommit); - expect(db.values.values.whereType().length, 1); - - final Map pushEvent = generatePushEvent( - branch, - owner, - repository, - message: message, - sha: sha, - avatarUrl: avatarUrl, - username: username, - ); - await commitService.handlePushGithubRequest(pushEvent); - - expect(db.values.values.whereType().length, 1); - final Commit commit = db.values.values.whereType().single; - expect(commit, existingCommit); - }); - }); -} diff --git a/app_dart/test/service/config_test.dart b/app_dart/test/service/config_test.dart deleted file mode 100644 index c65313913..000000000 --- a/app_dart/test/service/config_test.dart +++ /dev/null @@ -1,60 +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:typed_data'; - -import 'package:cocoon_service/cocoon_service.dart'; -import 'package:github/github.dart'; -import 'package:test/test.dart'; - -import '../src/datastore/fake_datastore.dart'; - -void main() { - group('Config', () { - FakeDatastoreDB datastore; - late CacheService cacheService; - late Config config; - setUp(() { - datastore = FakeDatastoreDB(); - cacheService = CacheService(inMemory: true); - config = Config(datastore, cacheService); - }); - test('githubAppInstallations when builder config does not exist', () async { - const String configValue = '{"godofredoc/cocoon":{"installation_id":"123"}}'; - final Uint8List cachedValue = Uint8List.fromList(configValue.codeUnits); - - await cacheService.set( - Config.configCacheName, - 'githubapp_installations', - cachedValue, - ); - final Map installation = await config.githubAppInstallations; - expect(installation['godofredoc/cocoon']['installation_id'], equals('123')); - }); - - test('generateGithubToken pulls from cache', () async { - const String configValue = 'githubToken'; - final Uint8List cachedValue = Uint8List.fromList(configValue.codeUnits); - await cacheService.set( - Config.configCacheName, - 'githubToken-${Config.flutterSlug}', - cachedValue, - ); - - final String githubToken = await config.generateGithubToken(Config.flutterSlug); - expect(githubToken, 'githubToken'); - }); - - test('Returns the right flutter gold alert', () { - expect( - config.flutterGoldAlertConstant(RepositorySlug.full('flutter/flutter')), - contains('package:flutter'), - ); - expect( - config.flutterGoldAlertConstant(RepositorySlug.full('flutter/engine')), - isNot(contains('package:flutter')), - ); - }); - }); -} diff --git a/app_dart/test/service/datastore_test.dart b/app_dart/test/service/datastore_test.dart deleted file mode 100644 index 97de03de3..000000000 --- a/app_dart/test/service/datastore_test.dart +++ /dev/null @@ -1,247 +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:cocoon_service/src/model/appengine/commit.dart'; -import 'package:cocoon_service/src/model/appengine/task.dart'; -import 'package:cocoon_service/src/service/config.dart'; -import 'package:cocoon_service/src/service/datastore.dart'; -import 'package:gcloud/datastore.dart' as gcloud_datastore; -import 'package:gcloud/db.dart'; -import 'package:grpc/grpc.dart'; -import 'package:retry/retry.dart'; -import 'package:test/test.dart'; - -import '../src/datastore/fake_config.dart'; -import '../src/datastore/fake_datastore.dart'; -import '../src/utilities/entity_generators.dart'; - -class Counter { - int count = 0; - void increase() { - count = count + 1; - } - - int value() { - return count; - } -} - -void main() { - group('Datastore', () { - late FakeConfig config; - late FakeDatastoreDB db; - late DatastoreService datastoreService; - late Commit commit; - - setUp(() { - db = FakeDatastoreDB(); - config = FakeConfig(dbValue: db); - datastoreService = DatastoreService(config.db, 5); - commit = Commit( - key: config.db.emptyKey.append(Commit, id: 'abc_master'), - sha: 'abc_master', - repository: Config.flutterSlug.fullName, - branch: 'master', - ); - }); - - group('DatasourceService', () { - setUp(() {}); - - test('defaultProvider returns a DatasourceService object', () async { - expect(DatastoreService.defaultProvider(config.db), isA()); - }); - - test('queryRecentCommits', () async { - for (String branch in ['master', 'release']) { - final Commit commit = Commit( - key: config.db.emptyKey.append(Commit, id: 'abc_$branch'), - repository: Config.flutterSlug.fullName, - sha: 'abc_$branch', - branch: branch, - ); - config.db.values[commit.key] = commit; - } - // Defaults to master - List commits = await datastoreService.queryRecentCommits(slug: Config.flutterSlug).toList(); - expect(commits, hasLength(1)); - expect(commits[0].branch, equals('master')); - // Explicit branch - commits = await datastoreService.queryRecentCommits(branch: 'release', slug: Config.flutterSlug).toList(); - expect(commits, hasLength(1)); - expect(commits[0].branch, equals('release')); - }); - - test('queryRecentCommits with slug', () async { - for (String repo in ['flutter', 'engine']) { - final Commit commit = Commit( - key: config.db.emptyKey.append(Commit, id: 'flutter/$repo/main/abc'), - repository: 'flutter/$repo', - sha: 'abc', - branch: 'main', - ); - config.db.values[commit.key] = commit; - } - // Only retrieves flutter/flutter - List commits = - await datastoreService.queryRecentCommits(slug: Config.flutterSlug, branch: 'main').toList(); - expect(commits, hasLength(1)); - expect(commits.single.repository, equals('flutter/flutter')); - // Only retrieves flutter/engine - commits = await datastoreService.queryRecentCommits(branch: 'main', slug: Config.engineSlug).toList(); - expect(commits, hasLength(1)); - expect(commits.single.repository, equals('flutter/engine')); - }); - }); - - test('queryRecentCommits with repo default branch', () async { - for (String branch in ['master', 'main']) { - final Commit commit = Commit( - key: config.db.emptyKey.append(Commit, id: 'abc_$branch'), - repository: Config.engineSlug.fullName, - sha: 'abc_$branch', - branch: branch, - ); - config.db.values[commit.key] = commit; - } - // Pull from main, not master - final List commits = await datastoreService.queryRecentCommits(slug: Config.engineSlug).toList(); - expect(commits, hasLength(1)); - expect(commits[0].branch, equals('main')); - }); - - test('queryRecentTasks returns all tasks', () async { - const String branch = 'main'; - final Commit commit = Commit( - key: config.db.emptyKey.append(Commit, id: 'abc_$branch'), - repository: Config.engineSlug.fullName, - sha: 'abc_$branch', - branch: branch, - ); - const int taskNumber = 2; - for (int i = 0; i < taskNumber; i++) { - final Task task = generateTask( - i, - parent: commit, - ); - config.db.values[task.key] = task; - } - - config.db.values[commit.key] = commit; - final List datastoreTasks = await datastoreService - .queryRecentTasks( - slug: Config.engineSlug, - ) - .toList(); - expect(datastoreTasks, hasLength(taskNumber)); - }); - - test('Shard', () async { - // default maxEntityGroups = 5 - List>> shards = await datastoreService.shard(generateCommits(6)); - expect(shards, hasLength(2)); - expect(shards[0], hasLength(5)); - expect(shards[1], hasLength(1)); - // maxEntitygroups = 2 - datastoreService = DatastoreService(config.db, 2); - shards = await datastoreService.shard(generateCommits(3)); - expect(shards, hasLength(2)); - expect(shards[0], hasLength(2)); - expect(shards[1], hasLength(1)); - // maxEntityGroups = 1 - datastoreService = DatastoreService(config.db, 1); - shards = await datastoreService.shard(generateCommits(3)); - expect(shards, hasLength(3)); - expect(shards[0], hasLength(1)); - expect(shards[1], hasLength(1)); - expect(shards[2], hasLength(1)); - }); - - test('Insert', () async { - await datastoreService.insert([commit]); - expect(config.db.values[commit.key], equals(commit)); - }); - - test('LookupByKey', () async { - config.db.values[commit.key] = commit; - final List commits = await datastoreService.lookupByKey(>[commit.key]); - expect(commits, hasLength(1)); - expect(commits[0], equals(commit)); - }); - - test('LookupByValue', () async { - config.db.values[commit.key] = commit; - final Commit expected = await datastoreService.lookupByValue(commit.key); - expect(expected, equals(commit)); - }); - - test('WithTransaction', () async { - final String? expected = await datastoreService.withTransaction((Transaction transaction) async { - transaction.queueMutations(inserts: [commit]); - await transaction.commit(); - return 'success'; - }); - expect(expected, equals('success')); - expect(config.db.values[commit.key], equals(commit)); - }); - }); - - group('RunTransactionWithRetry', () { - late RetryOptions retryOptions; - - setUp(() { - retryOptions = const RetryOptions( - delayFactor: Duration(milliseconds: 1), - maxDelay: Duration(milliseconds: 2), - maxAttempts: 2, - ); - }); - - test('retriesOnGrpcError', () async { - final Counter counter = Counter(); - try { - await runTransactionWithRetries( - () async { - counter.increase(); - throw const GrpcError.aborted(); - }, - retryOptions: retryOptions, - ); - } catch (e) { - expect(e, isA()); - } - expect(counter.value(), greaterThan(1)); - }); - test('retriesTransactionAbortedError', () async { - final Counter counter = Counter(); - try { - await runTransactionWithRetries( - () async { - counter.increase(); - throw gcloud_datastore.TransactionAbortedError(); - }, - retryOptions: retryOptions, - ); - } catch (e) { - expect(e, isA()); - } - expect(counter.value(), greaterThan(1)); - }); - test('DoesNotRetryOnSuccess', () async { - final Counter counter = Counter(); - await runTransactionWithRetries( - () async { - counter.increase(); - }, - retryOptions: retryOptions, - ); - expect(counter.value(), equals(1)); - }); - }); -} - -/// Helper function to generate fake commits -List generateCommits(int i) { - return List.generate(i, (int i) => Commit(repository: 'flutter/flutter', sha: '$i')); -} diff --git a/app_dart/test/service/gerrit_service_test.dart b/app_dart/test/service/gerrit_service_test.dart deleted file mode 100644 index 29c4e19a1..000000000 --- a/app_dart/test/service/gerrit_service_test.dart +++ /dev/null @@ -1,257 +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:io'; - -import 'package:cocoon_service/src/model/gerrit/commit.dart'; -import 'package:cocoon_service/src/request_handling/exceptions.dart'; -import 'package:cocoon_service/src/service/branch_service.dart'; -import 'package:cocoon_service/src/service/config.dart'; -import 'package:cocoon_service/src/service/gerrit_service.dart'; -import 'package:github/github.dart'; -import 'package:http/http.dart' as http; -import 'package:http/http.dart'; -import 'package:http/testing.dart'; -import 'package:test/test.dart'; - -import '../src/datastore/fake_config.dart'; -import '../src/service/fake_auth_client.dart'; -import '../src/utilities/matchers.dart'; - -void main() { - late MockClient mockHttpClient; - late GerritService gerritService; - group('getBranches', () { - test('Too many retries raise an exception', () async { - mockHttpClient = MockClient((_) async => http.Response(')]}\'\n[]', HttpStatus.forbidden)); - gerritService = GerritService(config: FakeConfig(), httpClient: mockHttpClient); - try { - await gerritService.branches( - 'myhost', - 'a/b/c', - filterRegex: 'flutter-*', - ); - } catch (e) { - expect(e, isA()); - } - }); - test('Returns a list of branches', () async { - Request? requestAux; - const String body = - ')]}\'\n[{"web_links":[{"name":"browse","url":"https://a.com/branch_a","target":"_blank"}],"ref":"refs/heads/branch_a","revision":"0bc"}]'; - mockHttpClient = MockClient((Request request) async { - requestAux = request; - return http.Response(body, HttpStatus.ok); - }); - gerritService = GerritService(config: FakeConfig(), httpClient: mockHttpClient); - final List branches = await gerritService.branches( - 'myhost', - 'a/b/c', - filterRegex: 'flutter-*|fuchsia*', - ); - expect(branches, equals(['refs/heads/branch_a'])); - expect(requestAux!.url.queryParameters, equals({'r': 'flutter-*|fuchsia*'})); - }); - - test('No results return an empty list', () async { - mockHttpClient = MockClient((_) async => http.Response(')]}\'\n[]', HttpStatus.ok)); - gerritService = GerritService(config: FakeConfig(), httpClient: mockHttpClient); - final List branches = await gerritService.branches( - 'myhost', - 'a/b/c', - filterRegex: 'flutter-', - ); - expect(branches, equals([])); - }); - }); - - group('commits', () { - test('Returns a list of commits', () async { - mockHttpClient = MockClient((_) async => http.Response(commitsListJson, HttpStatus.ok)); - gerritService = GerritService(config: FakeConfig(), httpClient: mockHttpClient); - final Iterable commits = await gerritService.commits(Config.recipesSlug, 'main'); - expect(commits.length, 1); - final GerritCommit commit = commits.single; - expect(commit.author?.email, 'dash@flutter.dev'); - expect(commit.author?.name, 'Dash'); - expect(commit.author?.time, isNotNull); - expect(commit.committer?.email, 'flutter-scoped@luci-project-accounts.iam.gserviceaccount.com'); - expect(commit.committer?.name, 'CQ Bot Account'); - expect(commit.committer?.time, isNotNull); - final DateTime time = commit.author!.time!; - final DateTime expectedTime = DateTime(2023, 4, 20, 18, 00, 14); - expect(time, expectedTime); - }); - }); - - group('getCommit', () { - test('Returns a commit', () async { - mockHttpClient = MockClient((_) async => http.Response(getCommitJson, HttpStatus.ok)); - gerritService = GerritService(config: FakeConfig(), httpClient: mockHttpClient); - final GerritCommit? commit = await gerritService.getCommit( - RepositorySlug('flutter', 'flutter'), - '7a702db7c1c8dc057d95e0d23849c885b3463ff3', - ); - expect(commit, isNotNull); - expect(commit!.author?.email, 'dash@flutter.dev'); - expect(commit.author?.name, 'Dash'); - expect(commit.author?.time, isNotNull); - final DateTime time = commit.author!.time!; - final DateTime expectedTime = DateTime(2023, 4, 20, 18, 0, 14); - expect(time, expectedTime); - }); - - test('Uses percent-encoding for project name', () async { - mockHttpClient = MockClient((request) async { - expect(request.url.path, startsWith('/projects/mirrors%2Fflutter')); - return http.Response(getCommitJson, HttpStatus.ok); - }); - gerritService = GerritService(config: FakeConfig(), httpClient: mockHttpClient); - final commit = await gerritService.getCommit( - RepositorySlug('flutter', 'mirrors/flutter'), - '7a702db7c1c8dc057d95e0d23849c885b3463ff3', - ); - expect(commit, isNotNull); - }); - }); - - group('findMirroredCommit', () { - test('Uses matching slug for GoB mirror', () async { - mockHttpClient = MockClient((request) async { - expect(request.url.path, startsWith('/projects/mirrors%2Fpackages')); - return http.Response(getCommitJson, HttpStatus.ok); - }); - gerritService = GerritService(config: FakeConfig(), httpClient: mockHttpClient); - final commit = await gerritService.findMirroredCommit( - RepositorySlug('flutter', 'packages'), - '7a702db7c1c8dc057d95e0d23849c885b3463ff3', - ); - expect(commit, isNotNull); - }); - }); - - group('createBranch', () { - test('ok response', () async { - mockHttpClient = MockClient((_) async => http.Response(createBranchJson, HttpStatus.ok)); - gerritService = GerritService( - config: FakeConfig(), - httpClient: mockHttpClient, - authClientProvider: ({ - Client? baseClient, - required List scopes, - }) async => - FakeAuthClient(baseClient!), - retryDelay: Duration.zero, - ); - - await gerritService.createBranch( - Config.recipesSlug, - 'flutter-2.13-candidate.0', - '00439ab49a991db42595f14078adb9811a6f60c6', - ); - }); - - test('unexpected response', () async { - mockHttpClient = MockClient((_) async => http.Response(createBranchJson, HttpStatus.ok)); - gerritService = GerritService( - config: FakeConfig(), - httpClient: mockHttpClient, - authClientProvider: ({ - Client? baseClient, - required List scopes, - }) async => - FakeAuthClient(baseClient!), - retryDelay: Duration.zero, - ); - expect( - () async => gerritService.createBranch(Config.recipesSlug, 'flutter-2.13-candidate.0', 'abc'), - throwsExceptionWith('Failed to create branch'), - ); - }); - - test('retries non-200 responses', () async { - int attempts = 0; - mockHttpClient = MockClient((_) async { - attempts = attempts + 1; - // Only send a failed response on the first attempt - if (attempts == 1) { - return http.Response('error', HttpStatus.internalServerError); - } - return http.Response(createBranchJson, HttpStatus.accepted); - }); - gerritService = GerritService( - config: FakeConfig(), - httpClient: mockHttpClient, - authClientProvider: ({ - Client? baseClient, - required List scopes, - }) async => - FakeAuthClient(baseClient!), - retryDelay: Duration.zero, - ); - await gerritService.createBranch( - Config.recipesSlug, - 'flutter-2.13-candidate.0', - '00439ab49a991db42595f14078adb9811a6f60c6', - ); - expect(attempts, 2); - }); - }); -} - -const String commitsListJson = ''')]}' -{ - "log": [ - { - "commit": "7a702db7c1c8dc057d95e0d23849c885b3463ff3", - "tree": "9c1529c1e92b3431534dcbf01d2b63547b73b005", - "parents": [ - "289edf3f90678e7ce1319aa1e8a57d7506c461d1" - ], - "author": { - "name": "Dash", - "email": "dash@flutter.dev", - "time": "Tue Apr 20 18:00:14 2023 +0000" - }, - "committer": { - "name": "CQ Bot Account", - "email": "flutter-scoped@luci-project-accounts.iam.gserviceaccount.com", - "time": "Tue Apr 20 18:00:14 2023 +0000" - }, - "message": "My first recipe change\\n\\ntested through `led get-builder" - } - ], - "next": "00439ab49a991db42595f14078adb9811a6f60c6" -} -'''; - -const String createBranchJson = ''')]}' -{ - "revision": "00439ab49a991db42595f14078adb9811a6f60c6" -} -'''; - -const String getCommitJson = ''')]}' -{ - "commit": "7a702db7c1c8dc057d95e0d23849c885b3463ff3", - "parents": [ - { - "commit": "1eee2c9d8f352483781e772f35dc586a69ff5646", - "subject": "Migrate contributor agreements to All-Projects." - } - ], - "author": { - "name": "Dash", - "email": "dash@flutter.dev", - "time": "Wed Apr 20 18:00:14 2023 +0000" - }, - "committer": { - "name": "CQ Bot Account", - "email": "flutter-scoped@luci-project-accounts.iam.gserviceaccount.com", - "time": "Wed Apr 20 18:00:14 2023 +0000" - }, - "subject": "Use an EventBus to manage star icons", - "message": "Use an EventBus to manage star icons\\n\\nImage widgets that need to ..." -} -'''; diff --git a/app_dart/test/service/github_checks_service_test.dart b/app_dart/test/service/github_checks_service_test.dart deleted file mode 100644 index a8ef8051f..000000000 --- a/app_dart/test/service/github_checks_service_test.dart +++ /dev/null @@ -1,286 +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 'package:cocoon_service/src/model/ci_yaml/target.dart'; -import 'package:cocoon_service/src/model/luci/buildbucket.dart'; -import 'package:cocoon_service/src/model/luci/push_message.dart' as push_message; -import 'package:cocoon_service/src/service/github_checks_service.dart'; - -import 'package:github/github.dart' as github; -import 'package:github/github.dart'; -import 'package:github/hooks.dart'; -import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; - -import '../model/github/checks_test_data.dart'; -import '../src/datastore/fake_config.dart'; -import '../src/service/fake_scheduler.dart'; -import '../src/utilities/entity_generators.dart'; -import '../src/utilities/mocks.dart'; - -void main() { - FakeConfig config; - late FakeScheduler scheduler; - MockGithubService mockGithubService; - late MockGithubChecksUtil mockGithubChecksUtil; - late MockLuciBuildService mockLuciBuildService; - late GithubChecksService githubChecksService; - late github.CheckRun checkRun; - late RepositorySlug slug; - - setUp(() { - mockGithubService = MockGithubService(); - mockLuciBuildService = MockLuciBuildService(); - when(mockGithubService.listFiles(any)).thenAnswer((_) async => []); - mockGithubChecksUtil = MockGithubChecksUtil(); - config = FakeConfig(githubService: mockGithubService, rollerAccountsValue: {'engine-flutter-autoroll'}); - githubChecksService = GithubChecksService( - config, - githubChecksUtil: mockGithubChecksUtil, - ); - slug = RepositorySlug('flutter', 'cocoon'); - scheduler = FakeScheduler( - config: config, - luciBuildService: mockLuciBuildService, - githubChecksUtil: mockGithubChecksUtil, - ciYaml: exampleConfig, - ); - checkRun = github.CheckRun.fromJson( - jsonDecode( - '{"name": "Linux Coverage", "id": 123, "external_id": "678", "status": "completed", "started_at": "2020-05-10T02:49:31Z", "head_sha": "the_sha", "check_suite": {"id": 456}}', - ) as Map, - ); - final Map checkRuns = {'Cocoon': checkRun}; - when(mockGithubChecksUtil.allCheckRuns(any, any)).thenAnswer((_) async { - return checkRuns; - }); - }); - - group('handleCheckSuiteEvent', () { - test('requested triggers all builds', () async { - final CheckSuiteEvent checkSuiteEvent = - CheckSuiteEvent.fromJson(jsonDecode(checkSuiteString) as Map); - when(mockGithubChecksUtil.createCheckRun(any, any, any, any, output: anyNamed('output'))) - .thenAnswer((_) async => generateCheckRun(1)); - final PullRequest pullRequest = generatePullRequest(id: 758); - await githubChecksService.handleCheckSuite(pullRequest, checkSuiteEvent, scheduler); - final List scheduledTargets = verify( - mockLuciBuildService.scheduleTryBuilds( - targets: captureAnyNamed('targets'), - pullRequest: anyNamed('pullRequest'), - checkSuiteEvent: anyNamed('checkSuiteEvent'), - ), - ).captured.single as List; - final Iterable scheduledTargetNames = scheduledTargets.map((Target target) => target.value.name); - expect(scheduledTargetNames, [ - 'Linux A', - 'Mac A', - 'Windows A', - ]); - }); - }); - - group('updateCheckStatus', () { - test('Userdata is empty', () async { - final push_message.BuildPushMessage buildMessage = - push_message.BuildPushMessage.fromJson(jsonDecode(buildPushMessageJsonTemplate('')) as Map); - final bool success = await githubChecksService.updateCheckStatus(buildMessage, mockLuciBuildService, slug); - expect(success, isFalse); - }); - test('Userdata does not contain check_run_id', () async { - final push_message.BuildPushMessage buildMessage = push_message.BuildPushMessage.fromJson( - jsonDecode(buildPushMessageJsonTemplate('{\\"retries\\": 1}')) as Map, - ); - final bool success = await githubChecksService.updateCheckStatus(buildMessage, mockLuciBuildService, slug); - expect(success, isFalse); - }); - test('Userdata contain check_run_id', () async { - when(mockGithubChecksUtil.getCheckRun(any, any, any)).thenAnswer((_) async => checkRun); - when( - mockLuciBuildService.getBuildById( - '8905920700440101120', - fields: 'id,builder,summaryMarkdown', - ), - ).thenAnswer( - (_) async => const Build( - id: '8905920700440101120', - builderId: BuilderId(bucket: 'luci.flutter.prod', project: 'flutter', builder: 'Linux Coverage'), - summaryMarkdown: 'test summary', - ), - ); - final push_message.BuildPushMessage buildPushMessage = push_message.BuildPushMessage.fromJson( - jsonDecode( - buildPushMessageJsonTemplate('{\\"check_run_id\\": 123,' - '\\"repo_owner\\": \\"flutter\\",' - '\\"repo_name\\": \\"cocoon\\"}'), - ) as Map, - ); - await githubChecksService.updateCheckStatus(buildPushMessage, mockLuciBuildService, slug); - final github.CheckRun checkRunCaptured = await verify( - mockGithubChecksUtil.updateCheckRun( - any, - any, - captureAny, - status: anyNamed('status'), - conclusion: anyNamed('conclusion'), - detailsUrl: anyNamed('detailsUrl'), - output: anyNamed('output'), - ), - ).captured.first; - expect(checkRunCaptured.id, checkRun.id); - expect(checkRunCaptured.name, checkRun.name); - }); - test('Should rerun a failed task for a roller account', () async { - when(mockGithubChecksUtil.getCheckRun(any, any, any)).thenAnswer((_) async => checkRun); - final push_message.BuildPushMessage buildPushMessage = push_message.BuildPushMessage.fromJson( - jsonDecode( - buildPushMessageJsonTemplate('{\\"check_run_id\\": 1,' - '\\"repo_owner\\": \\"flutter\\",' - '\\"repo_name\\": \\"cocoon\\",' - '\\"user_login\\": \\"engine-flutter-autoroll\\"}'), - ) as Map, - ); - when( - mockLuciBuildService.rescheduleBuild( - builderName: 'Linux Coverage', - buildPushMessage: buildPushMessage, - rescheduleAttempt: 1, - ), - ).thenAnswer( - (_) async => const Build( - id: '8905920700440101120', - builderId: BuilderId(bucket: 'luci.flutter.prod', project: 'flutter', builder: 'Linux Coverage'), - ), - ); - expect(checkRun.status, github.CheckRunStatus.completed); - await githubChecksService.updateCheckStatus(buildPushMessage, mockLuciBuildService, slug, rescheduled: true); - final List captured = verify( - mockGithubChecksUtil.updateCheckRun( - any, - any, - captureAny, - status: captureAnyNamed('status'), - conclusion: captureAnyNamed('conclusion'), - detailsUrl: anyNamed('detailsUrl'), - output: anyNamed('output'), - ), - ).captured; - expect(captured.length, 3); - expect(captured[1], github.CheckRunStatus.queued); - expect(captured[2], isNull); - }); - test('Should not rerun a failed task for a non roller account', () async { - when(mockGithubChecksUtil.getCheckRun(any, any, any)).thenAnswer((_) async => checkRun); - final push_message.BuildPushMessage buildPushMessage = push_message.BuildPushMessage.fromJson( - jsonDecode( - buildPushMessageJsonTemplate('{\\"check_run_id\\": 1,' - '\\"repo_owner\\": \\"flutter\\",' - '\\"repo_name\\": \\"cocoon\\",' - '\\"user_login\\": \\"test-account\\"}'), - ) as Map, - ); - when( - mockLuciBuildService.rescheduleBuild( - builderName: 'Linux Coverage', - buildPushMessage: buildPushMessage, - rescheduleAttempt: 1, - ), - ).thenAnswer( - (_) async => const Build( - id: '8905920700440101120', - builderId: BuilderId(bucket: 'luci.flutter.prod', project: 'flutter', builder: 'Linux Coverage'), - ), - ); - when( - mockLuciBuildService.getBuildById( - '8905920700440101120', - fields: 'id,builder,summaryMarkdown', - ), - ).thenAnswer( - (_) async => const Build( - id: '8905920700440101120', - builderId: BuilderId(bucket: 'luci.flutter.prod', project: 'flutter', builder: 'Linux Coverage'), - summaryMarkdown: 'test summary', - ), - ); - await githubChecksService.updateCheckStatus(buildPushMessage, mockLuciBuildService, slug); - final List captured = verify( - mockGithubChecksUtil.updateCheckRun( - any, - any, - any, - status: captureAnyNamed('status'), - conclusion: captureAnyNamed('conclusion'), - detailsUrl: anyNamed('detailsUrl'), - output: captureAnyNamed('output'), - ), - ).captured; - expect(captured.length, 3); - expect(captured[0], github.CheckRunStatus.completed); - expect(captured[1], github.CheckRunConclusion.failure); - }); - }); - - group('getGithubSummary', () { - test('nonempty summaryMarkdown', () async { - const String summaryMarkdown = 'test'; - const String expectedSummary = '$kGithubSummary$summaryMarkdown'; - expect(githubChecksService.getGithubSummary(summaryMarkdown), expectedSummary); - }); - - test('empty summaryMarkdown', () async { - const String expectedSummary = '${kGithubSummary}Empty summaryMarkdown'; - expect(githubChecksService.getGithubSummary(null), expectedSummary); - }); - - test('really large summaryMarkdown', () async { - String summaryMarkdown = ''; - for (int i = 0; i < 20000; i++) { - summaryMarkdown += 'test '; - } - expect(githubChecksService.getGithubSummary(summaryMarkdown), startsWith('$kGithubSummary[TRUNCATED...]')); - expect(githubChecksService.getGithubSummary(summaryMarkdown).length, lessThan(65535)); - }); - }); -} - -String buildPushMessageJsonTemplate(String jsonUserData) => '''{ - "build": { - "bucket": "luci.flutter.prod", - "canary": false, - "canary_preference": "PROD", - "created_by": "user:dnfield@google.com", - "created_ts": "1565049186247524", - "experimental": true, - "id": "8905920700440101120", - "parameters_json": "{\\"builder_name\\": \\"Linux Coverage\\", \\"properties\\": {\\"git_ref\\": \\"refs/pull/37647/head\\", \\"git_url\\": \\"https://github.com/flutter/flutter\\"}}", - "project": "flutter", - "result_details_json": "{\\"properties\\": {}, \\"swarming\\": {\\"bot_dimensions\\": {\\"caches\\": [\\"flutter_openjdk_install\\", \\"git\\", \\"goma_v2\\", \\"vpython\\"], \\"cores\\": [\\"8\\"], \\"cpu\\": [\\"x86\\", \\"x86-64\\", \\"x86-64-Broadwell_GCE\\", \\"x86-64-avx2\\"], \\"gce\\": [\\"1\\"], \\"gpu\\": [\\"none\\"], \\"id\\": [\\"luci-flutter-prod-xenial-2-bnrz\\"], \\"image\\": [\\"chrome-xenial-19052201-9cb74617499\\"], \\"inside_docker\\": [\\"0\\"], \\"kvm\\": [\\"1\\"], \\"locale\\": [\\"en_US.UTF-8\\"], \\"machine_type\\": [\\"n1-standard-8\\"], \\"os\\": [\\"Linux\\", \\"Ubuntu\\", \\"Ubuntu-16.04\\"], \\"pool\\": [\\"luci.flutter.prod\\"], \\"python\\": [\\"2.7.12\\"], \\"server_version\\": [\\"4382-5929880\\"], \\"ssd\\": [\\"0\\"], \\"zone\\": [\\"us\\", \\"us-central\\", \\"us-central1\\", \\"us-central1-c\\"]}}}", - "service_account": "flutter-prod-builder@chops-service-accounts.iam.gserviceaccount.com", - "started_ts": "1565049193786080", - "status": "COMPLETED", - "result": "FAILURE", - "status_changed_ts": "1565049194386647", - "tags": [ - "build_address:luci.flutter.prod/Linux Coverage/1698", - "builder:Linux Coverage", - "buildset:pr/git/37647", - "buildset:sha/git/0d78fc94f890a64af140ce0a2671ac5fc636f59b", - "swarming_hostname:chromium-swarm.appspot.com", - "swarming_tag:log_location:logdog://logs.chromium.org/flutter/buildbucket/cr-buildbucket.appspot.com/8905920700440101120/+/annotations", - "swarming_tag:luci_project:flutter", - "swarming_tag:os:Linux", - "swarming_tag:recipe_name:flutter/flutter", - "swarming_tag:recipe_package:infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build", - "swarming_task_id:467d04f2f022d510" - ], - "updated_ts": "1565049194391321", - "url": "https://ci.chromium.org/b/8905920700440101120", - "utcnow_ts": "1565049194653640" - }, - "hostname": "cr-buildbucket.appspot.com", - "user_data": "$jsonUserData" -}'''; diff --git a/app_dart/test/service/github_service_test.dart b/app_dart/test/service/github_service_test.dart deleted file mode 100644 index 405c2c833..000000000 --- a/app_dart/test/service/github_service_test.dart +++ /dev/null @@ -1,110 +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:io'; - -import 'package:cocoon_service/src/service/github_service.dart'; - -import 'package:github/github.dart'; -import 'package:http/http.dart' as http; -import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; - -import '../src/utilities/mocks.dart'; - -void main() { - late GithubService githubService; - MockGitHub mockGitHub; - late RepositorySlug slug; - - const String branch = 'master'; - const int lastCommitTimestampMills = 100; - - const String authorName = 'Jane Doe'; - const String authorEmail = 'janedoe@example.com'; - const String authorDate = '2000-01-01T10:10:10Z'; - const String authorLogin = 'Username'; - const String authorAvatarUrl = 'http://example.com/avatar'; - const String commitMessage = 'commit message'; - - List shas; - - setUp(() { - shas = []; - mockGitHub = MockGitHub(); - githubService = GithubService(mockGitHub); - slug = RepositorySlug('flutter', 'flutter'); - final PostExpectation> whenGithubRequest = when( - mockGitHub.request( - 'GET', - '/repos/${slug.owner}/${slug.name}/commits', - headers: anyNamed('headers'), - params: anyNamed('params'), - body: anyNamed('body'), - statusCode: anyNamed('statusCode'), - ), - ); - whenGithubRequest.thenAnswer((_) async { - final List data = []; - for (String sha in shas) { - // https://developer.github.com/v3/repos/commits/#list-commits - data.add({ - 'sha': sha, - 'commit': { - 'message': commitMessage, - 'author': { - 'name': authorName, - 'email': authorEmail, - 'date': authorDate, - }, - }, - 'author': { - 'login': authorLogin, - 'avatar_url': authorAvatarUrl, - }, - }); - } - return http.Response(json.encode(data), HttpStatus.ok); - }); - }); - - test('listCommits decodes all relevant fields of each commit', () async { - shas = ['1']; - final List commits = await githubService.listBranchedCommits( - slug, - branch, - lastCommitTimestampMills, - ); - expect(commits, hasLength(1)); - final RepositoryCommit commit = commits.single; - expect(commit.sha, shas.single); - expect(commit.author, isNotNull); - expect(commit.author!.login, authorLogin); - expect(commit.author!.avatarUrl, authorAvatarUrl); - expect(commit.commit, isNotNull); - expect(commit.commit!.message, commitMessage); - expect(commit.commit!.committer, isNotNull); - expect(commit.commit!.committer!.name, authorName); - expect(commit.commit!.committer!.email, authorEmail); - }); - - test('searchIssuesAndPRs encodes query properly', () async { - mockGitHub = MockGitHub(); - final mockSearchService = MockSearchService(); - when(mockGitHub.search).thenReturn(mockSearchService); - when(mockSearchService.issues(any)).thenAnswer((invocation) { - expect( - invocation.positionalArguments[0], - '6afa96d84e2ecf6537f8ea76341d8ba397942e80%20repo%3Aflutter%2Fflutter', - ); - return const Stream.empty(); - }); - githubService = GithubService(mockGitHub); - await githubService.searchIssuesAndPRs( - slug, - '6afa96d84e2ecf6537f8ea76341d8ba397942e80', - ); - }); -} diff --git a/app_dart/test/service/luci_build_service_test.dart b/app_dart/test/service/luci_build_service_test.dart deleted file mode 100644 index 79e2d9674..000000000 --- a/app_dart/test/service/luci_build_service_test.dart +++ /dev/null @@ -1,1070 +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 'package:cocoon_service/cocoon_service.dart'; -import 'package:cocoon_service/src/model/appengine/commit.dart'; -import 'package:cocoon_service/src/model/appengine/task.dart'; -import 'package:cocoon_service/src/model/ci_yaml/target.dart'; -import 'package:cocoon_service/src/model/luci/buildbucket.dart'; -import 'package:cocoon_service/src/model/luci/push_message.dart' as push_message; -import 'package:cocoon_service/src/service/exceptions.dart'; -import 'package:cocoon_service/src/service/datastore.dart'; -import 'package:github/github.dart'; -import 'package:cocoon_service/src/model/github/checks.dart' as cocoon_checks; -import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; - -import '../src/datastore/fake_config.dart'; -import '../src/request_handling/fake_pubsub.dart'; -import '../src/service/fake_gerrit_service.dart'; -import '../src/service/fake_github_service.dart'; -import '../src/utilities/entity_generators.dart'; -import '../src/utilities/mocks.dart'; -import '../src/utilities/push_message.dart'; -import '../src/utilities/webhook_generators.dart'; - -void main() { - late CacheService cache; - late FakeConfig config; - FakeGithubService githubService; - late MockBuildBucketClient mockBuildBucketClient; - late LuciBuildService service; - late RepositorySlug slug; - late MockGithubChecksUtil mockGithubChecksUtil = MockGithubChecksUtil(); - late FakePubSub pubsub; - - final List targets = [ - generateTarget(1, properties: {'os': 'abc'}), - ]; - final PullRequest pullRequest = generatePullRequest(id: 1, repo: 'cocoon'); - - group('getBuilds', () { - final Build macBuild = generateBuild(999, name: 'Mac', status: Status.started); - final Build linuxBuild = generateBuild(998, name: 'Linux', bucket: 'try', status: Status.started); - - setUp(() { - cache = CacheService(inMemory: true); - githubService = FakeGithubService(); - config = FakeConfig(githubService: githubService); - mockBuildBucketClient = MockBuildBucketClient(); - pubsub = FakePubSub(); - service = LuciBuildService( - config: config, - cache: cache, - buildBucketClient: mockBuildBucketClient, - gerritService: FakeGerritService(), - pubsub: pubsub, - ); - slug = RepositorySlug('flutter', 'cocoon'); - }); - - test('Null build', () async { - when(mockBuildBucketClient.batch(any)).thenAnswer((_) async { - return BatchResponse( - responses: [ - Response( - searchBuilds: SearchBuildsResponse( - builds: [macBuild], - ), - ), - ], - ); - }); - final Iterable builds = await service.getTryBuilds( - Config.flutterSlug, - 'shasha', - 'abcd', - ); - expect(builds.first, macBuild); - }); - - test('Existing prod build', () async { - when(mockBuildBucketClient.batch(any)).thenAnswer((_) async { - return const BatchResponse( - responses: [ - Response( - searchBuilds: SearchBuildsResponse( - builds: [], - ), - ), - ], - ); - }); - final Iterable builds = await service.getProdBuilds( - slug, - 'commit123', - 'abcd', - ); - expect(builds, isEmpty); - }); - - test('Existing try build', () async { - when(mockBuildBucketClient.batch(any)).thenAnswer((_) async { - return BatchResponse( - responses: [ - Response( - searchBuilds: SearchBuildsResponse( - builds: [linuxBuild], - ), - ), - ], - ); - }); - final Iterable builds = await service.getTryBuilds( - Config.flutterSlug, - 'shasha', - 'abcd', - ); - expect(builds.first, linuxBuild); - }); - - test('Existing try build by pull request', () async { - when(mockBuildBucketClient.batch(any)).thenAnswer((_) async { - return BatchResponse( - responses: [ - Response( - searchBuilds: SearchBuildsResponse( - builds: [linuxBuild], - ), - ), - ], - ); - }); - final Iterable builds = await service.getTryBuildsByPullRequest( - PullRequest(id: 998, base: PullRequestHead(repo: Repository(fullName: 'flutter/cocoon'))), - ); - expect(builds.first, linuxBuild); - }); - }); - - group('getBuilders', () { - setUp(() { - cache = CacheService(inMemory: true); - githubService = FakeGithubService(); - config = FakeConfig(githubService: githubService); - mockBuildBucketClient = MockBuildBucketClient(); - pubsub = FakePubSub(); - service = LuciBuildService( - config: config, - cache: cache, - buildBucketClient: mockBuildBucketClient, - gerritService: FakeGerritService(), - pubsub: pubsub, - ); - slug = RepositorySlug('flutter', 'flutter'); - }); - - test('with one rpc call', () async { - when(mockBuildBucketClient.listBuilders(any)).thenAnswer((_) async { - return const ListBuildersResponse( - builders: [ - BuilderItem(id: BuilderId(bucket: 'prod', project: 'flutter', builder: 'test1')), - BuilderItem(id: BuilderId(bucket: 'prod', project: 'flutter', builder: 'test2')), - ], - ); - }); - final Set builders = await service.getAvailableBuilderSet(); - expect(builders.length, 2); - expect(builders.contains('test1'), isTrue); - }); - - test('with more than one rpc calls', () async { - int retries = -1; - when(mockBuildBucketClient.listBuilders(any)).thenAnswer((_) async { - retries++; - if (retries == 0) { - return const ListBuildersResponse( - builders: [ - BuilderItem(id: BuilderId(bucket: 'prod', project: 'flutter', builder: 'test1')), - BuilderItem(id: BuilderId(bucket: 'prod', project: 'flutter', builder: 'test2')), - ], - nextPageToken: 'token', - ); - } else if (retries == 1) { - return const ListBuildersResponse( - builders: [ - BuilderItem(id: BuilderId(bucket: 'prod', project: 'flutter', builder: 'test3')), - BuilderItem(id: BuilderId(bucket: 'prod', project: 'flutter', builder: 'test4')), - ], - ); - } else { - return const ListBuildersResponse(builders: []); - } - }); - final Set builders = await service.getAvailableBuilderSet(); - expect(builders.length, 4); - expect(builders, {'test1', 'test2', 'test3', 'test4'}); - }); - }); - - group('buildsForRepositoryAndPr', () { - final Build macBuild = generateBuild(999, name: 'Mac', status: Status.started); - final Build linuxBuild = generateBuild(998, name: 'Linux', status: Status.started); - - setUp(() { - cache = CacheService(inMemory: true); - githubService = FakeGithubService(); - config = FakeConfig(githubService: githubService); - mockBuildBucketClient = MockBuildBucketClient(); - pubsub = FakePubSub(); - service = LuciBuildService( - config: config, - cache: cache, - buildBucketClient: mockBuildBucketClient, - pubsub: pubsub, - ); - slug = RepositorySlug('flutter', 'cocoon'); - }); - - test('Empty responses are handled correctly', () async { - when(mockBuildBucketClient.batch(any)).thenAnswer((_) async { - return const BatchResponse( - responses: [ - Response( - searchBuilds: SearchBuildsResponse( - builds: [], - ), - ), - ], - ); - }); - final Iterable builds = await service.getTryBuilds( - RepositorySlug.full(pullRequest.base!.repo!.fullName), - pullRequest.head!.sha!, - null, - ); - expect(builds, isEmpty); - }); - - test('Response returning a couple of builds', () async { - when(mockBuildBucketClient.batch(any)).thenAnswer((_) async { - return BatchResponse( - responses: [ - Response( - searchBuilds: SearchBuildsResponse( - builds: [macBuild], - ), - ), - Response( - searchBuilds: SearchBuildsResponse( - builds: [linuxBuild], - ), - ), - ], - ); - }); - final Iterable builds = await service.getTryBuilds( - RepositorySlug.full(pullRequest.base!.repo!.fullName), - pullRequest.head!.sha!, - null, - ); - expect(builds, equals({macBuild, linuxBuild})); - }); - }); - - group('scheduleBuilds', () { - setUp(() { - cache = CacheService(inMemory: true); - githubService = FakeGithubService(); - config = FakeConfig(githubService: githubService); - mockBuildBucketClient = MockBuildBucketClient(); - mockGithubChecksUtil = MockGithubChecksUtil(); - pubsub = FakePubSub(); - service = LuciBuildService( - config: config, - cache: cache, - buildBucketClient: mockBuildBucketClient, - githubChecksUtil: mockGithubChecksUtil, - gerritService: FakeGerritService(branchesValue: ['master']), - pubsub: pubsub, - ); - slug = RepositorySlug('flutter', 'cocoon'); - }); - - test('schedule try builds successfully', () async { - final PullRequest pullRequest = generatePullRequest(); - when(mockBuildBucketClient.batch(any)).thenAnswer((_) async { - return BatchResponse( - responses: [ - Response( - scheduleBuild: generateBuild(1), - ), - ], - ); - }); - when(mockGithubChecksUtil.createCheckRun(any, any, any, any)) - .thenAnswer((_) async => generateCheckRun(1, name: 'Linux 1')); - final List scheduledTargets = await service.scheduleTryBuilds( - pullRequest: pullRequest, - targets: targets, - ); - final Iterable scheduledTargetNames = scheduledTargets.map((Target target) => target.value.name); - expect(scheduledTargetNames, ['Linux 1']); - final BatchRequest batchRequest = pubsub.messages.single as BatchRequest; - expect(batchRequest.requests!.single.scheduleBuild, isNotNull); - - final ScheduleBuildRequest scheduleBuild = batchRequest.requests!.single.scheduleBuild!; - expect(scheduleBuild.builderId.bucket, 'try'); - expect(scheduleBuild.builderId.builder, 'Linux 1'); - expect(scheduleBuild.notify?.pubsubTopic, 'projects/flutter-dashboard/topics/luci-builds'); - final Map userData = - jsonDecode(String.fromCharCodes(base64Decode(scheduleBuild.notify!.userData!))) as Map; - expect(userData, { - 'repo_owner': 'flutter', - 'repo_name': 'flutter', - 'user_agent': 'flutter-cocoon', - 'check_run_id': 1, - 'commit_sha': 'abc', - 'commit_branch': 'master', - 'builder_name': 'Linux 1', - }); - - final Map properties = scheduleBuild.properties!; - final List dimensions = scheduleBuild.dimensions!; - expect(properties, { - 'os': 'abc', - 'dependencies': [], - 'bringup': false, - 'git_branch': 'master', - 'git_url': 'https://github.com/flutter/flutter', - 'git_ref': 'refs/pull/123/head', - 'exe_cipd_version': 'refs/heads/main', - }); - expect(dimensions.length, 1); - expect(dimensions[0].key, 'os'); - expect(dimensions[0].value, 'abc'); - }); - - test('schedule try builds with github build labels successfully', () async { - final issueLabels = [ - IssueLabel(name: '${LuciBuildService.githubBuildLabelPrefix}hello'), - IssueLabel(name: '${LuciBuildService.githubBuildLabelPrefix}world'), - ]; - final PullRequest pullRequest = generatePullRequest(labels: issueLabels); - when(mockBuildBucketClient.batch(any)).thenAnswer((_) async { - return BatchResponse( - responses: [ - Response( - scheduleBuild: generateBuild(1), - ), - ], - ); - }); - when(mockGithubChecksUtil.createCheckRun(any, any, any, any)) - .thenAnswer((_) async => generateCheckRun(1, name: 'Linux 1')); - final List scheduledTargets = await service.scheduleTryBuilds( - pullRequest: pullRequest, - targets: targets, - ); - final Iterable scheduledTargetNames = scheduledTargets.map((Target target) => target.value.name); - expect(scheduledTargetNames, ['Linux 1']); - final BatchRequest batchRequest = pubsub.messages.single as BatchRequest; - expect(batchRequest.requests!.single.scheduleBuild, isNotNull); - - final ScheduleBuildRequest scheduleBuild = batchRequest.requests!.single.scheduleBuild!; - expect(scheduleBuild.builderId.bucket, 'try'); - expect(scheduleBuild.builderId.builder, 'Linux 1'); - expect(scheduleBuild.notify?.pubsubTopic, 'projects/flutter-dashboard/topics/luci-builds'); - final Map userData = - jsonDecode(String.fromCharCodes(base64Decode(scheduleBuild.notify!.userData!))) as Map; - expect(userData, { - 'repo_owner': 'flutter', - 'repo_name': 'flutter', - 'user_agent': 'flutter-cocoon', - 'check_run_id': 1, - 'commit_sha': 'abc', - 'commit_branch': 'master', - 'builder_name': 'Linux 1', - }); - - final Map properties = scheduleBuild.properties!; - final List dimensions = scheduleBuild.dimensions!; - expect(properties, { - 'os': 'abc', - 'dependencies': [], - 'bringup': false, - 'git_branch': 'master', - 'git_url': 'https://github.com/flutter/flutter', - 'git_ref': 'refs/pull/123/head', - 'exe_cipd_version': 'refs/heads/main', - LuciBuildService.propertiesGithubBuildLabelName: [ - '${LuciBuildService.githubBuildLabelPrefix}hello', - '${LuciBuildService.githubBuildLabelPrefix}world', - ], - }); - expect(dimensions.length, 1); - expect(dimensions[0].key, 'os'); - expect(dimensions[0].value, 'abc'); - }); - - test('Schedule builds no-ops when targets list is empty', () async { - await service.scheduleTryBuilds( - pullRequest: pullRequest, - targets: [], - ); - verifyNever(mockGithubChecksUtil.createCheckRun(any, any, any, any)); - }); - }); - - group('schedulePostsubmitBuilds', () { - setUp(() { - cache = CacheService(inMemory: true); - mockBuildBucketClient = MockBuildBucketClient(); - pubsub = FakePubSub(); - service = LuciBuildService( - config: FakeConfig(), - cache: cache, - buildBucketClient: mockBuildBucketClient, - githubChecksUtil: mockGithubChecksUtil, - pubsub: pubsub, - ); - }); - - test('schedule packages postsubmit builds successfully', () async { - final Commit commit = generateCommit(0); - when(mockGithubChecksUtil.createCheckRun(any, Config.packagesSlug, any, 'Linux 1')) - .thenAnswer((_) async => generateCheckRun(1)); - when(mockBuildBucketClient.listBuilders(any)).thenAnswer((_) async { - return const ListBuildersResponse( - builders: [ - BuilderItem(id: BuilderId(bucket: 'prod', project: 'flutter', builder: 'Linux 1')), - ], - ); - }); - final Tuple toBeScheduled = Tuple( - generateTarget( - 1, - properties: { - 'os': 'debian-10.12', - }, - slug: Config.packagesSlug, - ), - generateTask(1), - LuciBuildService.kDefaultPriority, - ); - await service.schedulePostsubmitBuilds( - commit: commit, - toBeScheduled: >[ - toBeScheduled, - ], - ); - // Only one batch request should be published - expect(pubsub.messages.length, 1); - final BatchRequest request = pubsub.messages.first as BatchRequest; - expect(request.requests?.single.scheduleBuild, isNotNull); - final ScheduleBuildRequest scheduleBuild = request.requests!.single.scheduleBuild!; - expect(scheduleBuild.builderId.bucket, 'prod'); - expect(scheduleBuild.builderId.builder, 'Linux 1'); - expect(scheduleBuild.notify?.pubsubTopic, 'projects/flutter-dashboard/topics/luci-builds-prod'); - final Map userData = - jsonDecode(String.fromCharCodes(base64Decode(scheduleBuild.notify!.userData!))) as Map; - expect(userData, { - 'commit_key': 'flutter/flutter/master/1', - 'task_key': '1', - 'check_run_id': 1, - 'commit_sha': '0', - 'commit_branch': 'master', - 'builder_name': 'Linux 1', - 'repo_owner': 'flutter', - 'repo_name': 'packages', - }); - final Map properties = scheduleBuild.properties!; - expect(properties, { - 'dependencies': [], - 'bringup': false, - 'git_branch': 'master', - 'exe_cipd_version': 'refs/heads/master', - 'os': 'debian-10.12', - }); - expect(scheduleBuild.exe, { - 'cipdVersion': 'refs/heads/master', - }); - expect(scheduleBuild.dimensions, isNotEmpty); - expect( - scheduleBuild.dimensions!.singleWhere((RequestedDimension dimension) => dimension.key == 'os').value, - 'debian-10.12', - ); - }); - - test('schedule postsubmit builds with correct userData for checkRuns', () async { - when(mockGithubChecksUtil.createCheckRun(any, any, any, any)) - .thenAnswer((_) async => generateCheckRun(1, name: 'Linux 1')); - final Commit commit = generateCommit(0, repo: 'packages'); - when(mockBuildBucketClient.listBuilders(any)).thenAnswer((_) async { - return const ListBuildersResponse( - builders: [ - BuilderItem(id: BuilderId(bucket: 'prod', project: 'flutter', builder: 'Linux 1')), - ], - ); - }); - final Tuple toBeScheduled = Tuple( - generateTarget( - 1, - properties: { - 'os': 'debian-10.12', - }, - slug: RepositorySlug('flutter', 'packages'), - ), - generateTask(1), - LuciBuildService.kDefaultPriority, - ); - await service.schedulePostsubmitBuilds( - commit: commit, - toBeScheduled: >[ - toBeScheduled, - ], - ); - // Only one batch request should be published - expect(pubsub.messages.length, 1); - final BatchRequest request = pubsub.messages.first as BatchRequest; - expect(request.requests?.single.scheduleBuild, isNotNull); - final ScheduleBuildRequest scheduleBuild = request.requests!.single.scheduleBuild!; - expect(scheduleBuild.builderId.bucket, 'prod'); - expect(scheduleBuild.builderId.builder, 'Linux 1'); - expect(scheduleBuild.notify?.pubsubTopic, 'projects/flutter-dashboard/topics/luci-builds-prod'); - final Map userData = - jsonDecode(String.fromCharCodes(base64Decode(scheduleBuild.notify!.userData!))) as Map; - expect(userData, { - 'commit_key': 'flutter/flutter/master/1', - 'task_key': '1', - 'check_run_id': 1, - 'commit_sha': '0', - 'commit_branch': 'master', - 'builder_name': 'Linux 1', - 'repo_owner': 'flutter', - 'repo_name': 'packages', - }); - }); - - test('return the orignal list when hitting buildbucket exception', () async { - final Commit commit = generateCommit(0, repo: 'packages'); - when(mockBuildBucketClient.listBuilders(any)).thenAnswer((_) async { - throw const BuildBucketException(1, 'error'); - }); - final Tuple toBeScheduled = Tuple( - generateTarget( - 1, - properties: { - 'os': 'debian-10.12', - }, - slug: RepositorySlug('flutter', 'packages'), - ), - generateTask(1), - LuciBuildService.kDefaultPriority, - ); - final List> results = await service.schedulePostsubmitBuilds( - commit: commit, - toBeScheduled: >[ - toBeScheduled, - ], - ); - expect(results, >[ - toBeScheduled, - ]); - }); - - test('reschedule using checkrun event fails gracefully', () async { - when(mockGithubChecksUtil.createCheckRun(any, any, any, any)) - .thenAnswer((_) async => generateCheckRun(1, name: 'Linux 1')); - - when(mockBuildBucketClient.batch(any)).thenAnswer((_) async { - return const BatchResponse( - responses: [ - Response( - searchBuilds: SearchBuildsResponse( - builds: [], - ), - ), - ], - ); - }); - - final pushMessage = generateCheckRunEvent(action: 'created', numberOfPullRequests: 1); - final Map jsonMap = json.decode(pushMessage.data!); - final Map jsonSubMap = json.decode(jsonMap['2']); - final cocoon_checks.CheckRunEvent checkRunEvent = cocoon_checks.CheckRunEvent.fromJson(jsonSubMap); - - expect( - () async => service.reschedulePostsubmitBuildUsingCheckRunEvent( - checkRunEvent, - commit: generateCommit(0), - task: generateTask(0), - target: generateTarget(0), - ), - throwsA(const TypeMatcher()), - ); - }); - - test('do not create postsubmit checkrun for bringup: true target', () async { - when(mockGithubChecksUtil.createCheckRun(any, any, any, any)) - .thenAnswer((_) async => generateCheckRun(1, name: 'Linux 1')); - final Commit commit = generateCommit(0, repo: Config.packagesSlug.name); - when(mockBuildBucketClient.listBuilders(any)).thenAnswer((_) async { - return const ListBuildersResponse( - builders: [ - BuilderItem(id: BuilderId(bucket: 'prod', project: 'flutter', builder: 'Linux 1')), - ], - ); - }); - final Tuple toBeScheduled = Tuple( - generateTarget( - 1, - properties: { - 'os': 'debian-10.12', - }, - bringup: true, - slug: Config.packagesSlug, - ), - generateTask(1, parent: commit), - LuciBuildService.kDefaultPriority, - ); - await service.schedulePostsubmitBuilds( - commit: commit, - toBeScheduled: >[ - toBeScheduled, - ], - ); - // Only one batch request should be published - expect(pubsub.messages.length, 1); - final BatchRequest request = pubsub.messages.first as BatchRequest; - expect(request.requests?.single.scheduleBuild, isNotNull); - final ScheduleBuildRequest scheduleBuild = request.requests!.single.scheduleBuild!; - expect(scheduleBuild.builderId.bucket, 'staging'); - expect(scheduleBuild.builderId.builder, 'Linux 1'); - expect(scheduleBuild.notify?.pubsubTopic, 'projects/flutter-dashboard/topics/luci-builds-prod'); - final Map userData = - jsonDecode(String.fromCharCodes(base64Decode(scheduleBuild.notify!.userData!))) as Map; - // No check run related data. - expect(userData, { - 'commit_key': 'flutter/packages/master/0', - 'task_key': '1', - }); - }); - - test('Skip non-existing builder', () async { - when(mockGithubChecksUtil.createCheckRun(any, any, any, any)) - .thenAnswer((_) async => generateCheckRun(1, name: 'Linux 1')); - final Commit commit = generateCommit(0); - when(mockGithubChecksUtil.createCheckRun(any, any, any, any)) - .thenAnswer((_) async => generateCheckRun(1, name: 'Linux 2')); - when(mockBuildBucketClient.listBuilders(any)).thenAnswer((_) async { - return const ListBuildersResponse( - builders: [ - BuilderItem(id: BuilderId(bucket: 'prod', project: 'flutter', builder: 'Linux 2')), - ], - ); - }); - final Tuple toBeScheduled1 = Tuple( - generateTarget( - 1, - properties: { - 'os': 'debian-10.12', - }, - ), - generateTask(1), - LuciBuildService.kDefaultPriority, - ); - final Tuple toBeScheduled2 = Tuple( - generateTarget( - 2, - properties: { - 'os': 'debian-10.12', - }, - ), - generateTask(1), - LuciBuildService.kDefaultPriority, - ); - await service.schedulePostsubmitBuilds( - commit: commit, - toBeScheduled: >[ - toBeScheduled1, - toBeScheduled2, - ], - ); - expect(pubsub.messages.length, 1); - final BatchRequest request = pubsub.messages.first as BatchRequest; - // Only existing builder: `Linux 2` is scheduled. - expect(request.requests?.length, 1); - expect(request.requests?.single.scheduleBuild, isNotNull); - final ScheduleBuildRequest scheduleBuild = request.requests!.single.scheduleBuild!; - expect(scheduleBuild.builderId.bucket, 'prod'); - expect(scheduleBuild.builderId.builder, 'Linux 2'); - }); - }); - - group('schedulePresubmitBuilds', () { - setUp(() { - cache = CacheService(inMemory: true); - mockBuildBucketClient = MockBuildBucketClient(); - pubsub = FakePubSub(); - service = LuciBuildService( - config: FakeConfig(), - cache: cache, - buildBucketClient: mockBuildBucketClient, - githubChecksUtil: mockGithubChecksUtil, - pubsub: pubsub, - ); - }); - test('reschedule using checkrun event', () async { - when(mockGithubChecksUtil.createCheckRun(any, any, any, any)) - .thenAnswer((_) async => generateCheckRun(1, name: 'Linux 1')); - - when(mockBuildBucketClient.batch(any)).thenAnswer((_) async { - return BatchResponse( - responses: [ - Response( - searchBuilds: SearchBuildsResponse( - builds: [ - generateBuild( - 1, - name: 'Linux', - status: Status.ended, - tags: { - 'buildset': ['pr/git/123'], - 'cipd_version': ['refs/heads/main'], - 'github_link': ['https://github.com/flutter/flutter/pull/1'], - }, - input: const Input(properties: {'test': 'abc'}), - ), - ], - ), - ), - ], - ); - }); - when(mockBuildBucketClient.scheduleBuild(any)).thenAnswer((_) async => generateBuild(1)); - - final pushMessage = generateCheckRunEvent(action: 'created', numberOfPullRequests: 1); - final Map jsonMap = json.decode(pushMessage.data!); - final Map jsonSubMap = json.decode(jsonMap['2']); - final cocoon_checks.CheckRunEvent checkRunEvent = cocoon_checks.CheckRunEvent.fromJson(jsonSubMap); - - await service.reschedulePresubmitBuildUsingCheckRunEvent( - checkRunEvent, - ); - final List captured = verify( - mockBuildBucketClient.scheduleBuild( - captureAny, - ), - ).captured; - expect(captured.length, 1); - final ScheduleBuildRequest scheduleBuildRequest = captured[0] as ScheduleBuildRequest; - final Map userData = - jsonDecode(String.fromCharCodes(base64Decode(scheduleBuildRequest.notify!.userData!))) - as Map; - expect(userData, { - 'check_run_id': 1, - 'commit_branch': 'master', - 'commit_sha': 'ec26c3e57ca3a959ca5aad62de7213c562f8c821', - 'repo_owner': 'flutter', - 'repo_name': 'flutter', - 'user_agent': 'flutter-cocoon', - }); - }); - }); - - group('cancelBuilds', () { - setUp(() { - cache = CacheService(inMemory: true); - config = FakeConfig(); - mockBuildBucketClient = MockBuildBucketClient(); - pubsub = FakePubSub(); - service = LuciBuildService( - config: config, - cache: cache, - buildBucketClient: mockBuildBucketClient, - pubsub: pubsub, - ); - slug = RepositorySlug('flutter', 'cocoon'); - }); - - test('Cancel builds when build list is empty', () async { - when(mockBuildBucketClient.batch(any)).thenAnswer((_) async { - return const BatchResponse( - responses: [], - ); - }); - await service.cancelBuilds(pullRequest, 'new builds'); - // This is okay, it is getting called twice when it runs cancel builds - // because the call is no longer being short-circuited. It calls batch in - // tryBuildsForPullRequest and it calls in the top level cancelBuilds - // function. - verify(mockBuildBucketClient.batch(any)).called(1); - }); - - test('Cancel builds that are scheduled', () async { - when(mockBuildBucketClient.batch(any)).thenAnswer((_) async { - return BatchResponse( - responses: [ - Response( - searchBuilds: SearchBuildsResponse( - builds: [ - generateBuild(998, name: 'Linux', status: Status.started), - ], - ), - ), - ], - ); - }); - await service.cancelBuilds(pullRequest, 'new builds'); - expect( - verify(mockBuildBucketClient.batch(captureAny)).captured[1].requests[0].cancelBuild.toJson(), - json.decode('{"id": "998", "summaryMarkdown": "new builds"}'), - ); - }); - }); - - group('failedBuilds', () { - setUp(() { - cache = CacheService(inMemory: true); - githubService = FakeGithubService(); - config = FakeConfig(githubService: githubService); - mockBuildBucketClient = MockBuildBucketClient(); - pubsub = FakePubSub(); - service = LuciBuildService( - config: config, - cache: cache, - buildBucketClient: mockBuildBucketClient, - pubsub: pubsub, - ); - slug = RepositorySlug('flutter', 'flutter'); - }); - - test('Failed builds from an empty list', () async { - when(mockBuildBucketClient.batch(any)).thenAnswer((_) async { - return const BatchResponse( - responses: [], - ); - }); - final List result = await service.failedBuilds(pullRequest, []); - expect(result, isEmpty); - }); - - test('Failed builds from a list of builds with failures', () async { - when(mockBuildBucketClient.batch(any)).thenAnswer((_) async { - return BatchResponse( - responses: [ - Response( - searchBuilds: SearchBuildsResponse( - builds: [ - generateBuild(998, name: 'Linux 1', status: Status.failure), - ], - ), - ), - ], - ); - }); - final List result = await service.failedBuilds(pullRequest, [generateTarget(1)]); - expect(result, hasLength(1)); - }); - }); - - group('rescheduleBuild', () { - late push_message.BuildPushMessage buildPushMessage; - - setUp(() { - cache = CacheService(inMemory: true); - config = FakeConfig(); - mockBuildBucketClient = MockBuildBucketClient(); - pubsub = FakePubSub(); - service = LuciBuildService( - config: config, - cache: cache, - buildBucketClient: mockBuildBucketClient, - pubsub: pubsub, - ); - final Map json = jsonDecode( - buildPushMessageString( - 'COMPLETED', - result: 'FAILURE', - builderName: 'Linux Host Engine', - userData: '{}', - ), - ) as Map; - buildPushMessage = push_message.BuildPushMessage.fromJson(json); - }); - - test('Reschedule an existing build', () async { - when(mockBuildBucketClient.scheduleBuild(any)).thenAnswer((_) async => generateBuild(1)); - final build = await service.rescheduleBuild( - builderName: 'mybuild', - buildPushMessage: buildPushMessage, - rescheduleAttempt: 2, - ); - expect(build.id, '1'); - expect(build.status, Status.success); - final List captured = verify(mockBuildBucketClient.scheduleBuild(captureAny)).captured; - expect(captured.length, 1); - final ScheduleBuildRequest scheduleBuildRequest = captured[0] as ScheduleBuildRequest; - // This is to validate `scheduleBuildRequest` can be json.encoded correctly. - // It complains when some non-String typed data exists. - expect(json.encode(scheduleBuildRequest), isNotNull); - expect(scheduleBuildRequest.tags!.containsKey('current_attempt'), true); - expect(scheduleBuildRequest.tags!['current_attempt'], ['2']); - }); - }); - - group('checkRerunBuilder', () { - late Commit commit; - late Commit totCommit; - late DatastoreService datastore; - late MockGithubChecksUtil mockGithubChecksUtil; - setUp(() { - cache = CacheService(inMemory: true); - config = FakeConfig(); - mockBuildBucketClient = MockBuildBucketClient(); - mockGithubChecksUtil = MockGithubChecksUtil(); - when(mockGithubChecksUtil.createCheckRun(any, any, any, any, output: anyNamed('output'))) - .thenAnswer((realInvocation) async => generateCheckRun(1)); - pubsub = FakePubSub(); - service = LuciBuildService( - config: config, - cache: cache, - buildBucketClient: mockBuildBucketClient, - githubChecksUtil: mockGithubChecksUtil, - pubsub: pubsub, - ); - datastore = DatastoreService(config.db, 5); - }); - - test('Pass repo and properties correctly', () async { - totCommit = generateCommit(1, repo: 'engine', branch: 'main'); - config.db.values[totCommit.key] = totCommit; - config.maxLuciTaskRetriesValue = 1; - final Task task = generateTask( - 1, - status: Task.statusFailed, - parent: totCommit, - buildNumber: 1, - ); - final Target target = generateTarget(1); - expect(task.attempts, 1); - expect(task.status, Task.statusFailed); - final bool rerunFlag = await service.checkRerunBuilder( - commit: totCommit, - task: task, - target: target, - datastore: datastore, - ); - expect(pubsub.messages.length, 1); - final ScheduleBuildRequest scheduleBuildRequest = - (pubsub.messages.single as BatchRequest).requests!.single.scheduleBuild!; - final Map properties = scheduleBuildRequest.properties!; - for (String key in Config.defaultProperties.keys) { - expect(properties.containsKey(key), true); - } - expect(scheduleBuildRequest.priority, LuciBuildService.kRerunPriority); - expect(scheduleBuildRequest.gitilesCommit?.project, 'mirrors/engine'); - expect(rerunFlag, isTrue); - expect(task.attempts, 2); - expect(task.status, Task.statusInProgress); - }); - - test('Rerun a test failed builder', () async { - totCommit = generateCommit(1); - config.db.values[totCommit.key] = totCommit; - config.maxLuciTaskRetriesValue = 1; - final Task task = generateTask( - 1, - status: Task.statusFailed, - parent: totCommit, - buildNumber: 1, - ); - final Target target = generateTarget(1); - final bool rerunFlag = await service.checkRerunBuilder( - commit: totCommit, - task: task, - target: target, - datastore: datastore, - ); - expect(rerunFlag, isTrue); - }); - - test('Rerun an infra failed builder', () async { - totCommit = generateCommit(1); - config.db.values[totCommit.key] = totCommit; - config.maxLuciTaskRetriesValue = 1; - final Task task = generateTask( - 1, - status: Task.statusInfraFailure, - parent: totCommit, - buildNumber: 1, - ); - final Target target = generateTarget(1); - final bool rerunFlag = await service.checkRerunBuilder( - commit: totCommit, - task: task, - target: target, - datastore: datastore, - ); - expect(rerunFlag, isTrue); - }); - - test('Do not rerun a successful builder', () async { - totCommit = generateCommit(1); - config.db.values[totCommit.key] = totCommit; - config.maxLuciTaskRetriesValue = 1; - final Task task = generateTask( - 1, - status: Task.statusSucceeded, - parent: totCommit, - buildNumber: 1, - ); - final Target target = generateTarget(1); - final bool rerunFlag = await service.checkRerunBuilder( - commit: totCommit, - task: task, - target: target, - datastore: datastore, - ); - expect(rerunFlag, isFalse); - }); - - test('Do not rerun a builder exceeding retry limit', () async { - totCommit = generateCommit(1); - config.db.values[totCommit.key] = totCommit; - config.maxLuciTaskRetriesValue = 1; - final Task task = generateTask( - 1, - status: Task.statusInfraFailure, - parent: totCommit, - buildNumber: 1, - attempts: 2, - ); - final Target target = generateTarget(1); - final bool rerunFlag = await service.checkRerunBuilder( - commit: totCommit, - task: task, - target: target, - datastore: datastore, - ); - expect(rerunFlag, isFalse); - }); - - test('Do not rerun a builder when not tip of tree', () async { - totCommit = generateCommit(2, sha: 'def'); - commit = generateCommit(1, sha: 'abc'); - config.db.values[totCommit.key] = totCommit; - config.db.values[commit.key] = commit; - config.maxLuciTaskRetriesValue = 1; - final Task task = generateTask( - 1, - status: Task.statusInfraFailure, - parent: commit, - buildNumber: 1, - ); - final Target target = generateTarget(1); - final bool rerunFlag = await service.checkRerunBuilder( - commit: commit, - task: task, - target: target, - datastore: datastore, - ); - expect(rerunFlag, isFalse); - }); - }); -} diff --git a/app_dart/test/service/scheduler/graph_test.dart b/app_dart/test/service/scheduler/graph_test.dart deleted file mode 100644 index 2c3b96ad3..000000000 --- a/app_dart/test/service/scheduler/graph_test.dart +++ /dev/null @@ -1,372 +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:cocoon_service/src/model/ci_yaml/ci_yaml.dart'; -import 'package:cocoon_service/src/model/proto/internal/scheduler.pb.dart'; -import 'package:cocoon_service/src/service/config.dart'; - -import 'package:test/test.dart'; -import 'package:yaml/yaml.dart'; - -void main() { - group('scheduler config', () { - test('constructs graph with one target', () { - final YamlMap? singleTargetConfig = loadYaml(''' -enabled_branches: - - master -targets: - - name: A - builder: builderA - drone_dimensions: - - os=Linux - properties: - test: abc - ''') as YamlMap?; - final SchedulerConfig unCheckedSchedulerConfig = SchedulerConfig()..mergeFromProto3Json(singleTargetConfig); - final SchedulerConfig schedulerConfig = CiYaml( - slug: Config.flutterSlug, - branch: Config.defaultBranch(Config.flutterSlug), - config: unCheckedSchedulerConfig, - validate: true, - ).config; - expect(schedulerConfig.enabledBranches, ['master']); - expect(schedulerConfig.targets.length, 1); - final Target target = schedulerConfig.targets.first; - expect(target.bringup, false); - expect(target.name, 'A'); - expect(target.properties, { - 'test': 'abc', - }); - expect(target.scheduler, SchedulerSystem.cocoon); - expect(target.testbed, 'linux-vm'); - expect(target.timeout, 30); - expect(target.droneDimensions, ['os=Linux']); - }); - - test('throws exception when non-existent scheduler is given', () { - final YamlMap? targetWithNonexistentScheduler = loadYaml(''' -enabled_branches: - - master -targets: - - name: A - scheduler: dashatar - ''') as YamlMap?; - expect( - () { - final SchedulerConfig unCheckedSchedulerConfig = SchedulerConfig() - ..mergeFromProto3Json(targetWithNonexistentScheduler); - CiYaml( - slug: Config.flutterSlug, - branch: Config.defaultBranch(Config.flutterSlug), - config: unCheckedSchedulerConfig, - validate: true, - ).config; - }, - throwsA(isA()), - ); - }); - - test('constructs graph with dependency chain', () { - final YamlMap? dependentTargetConfig = loadYaml(''' -enabled_branches: - - master -targets: - - name: A - - name: B - dependencies: - - A - properties: - dependencies: >- - [ - {"dependency": "android_sdk", "version": "version:31v8"}, - {"dependency": "certs", "version": "version:9563bb"}, - {"dependency": "chrome_and_driver", "version": "version:96.2"}, - {"dependency": "open_jdk", "version": "11"} - ] - - name: C - dependencies: - - B - ''') as YamlMap?; - final SchedulerConfig unCheckedSchedulerConfig = SchedulerConfig()..mergeFromProto3Json(dependentTargetConfig); - final SchedulerConfig schedulerConfig = CiYaml( - slug: Config.flutterSlug, - branch: Config.defaultBranch(Config.flutterSlug), - config: unCheckedSchedulerConfig, - validate: true, - ).config; - expect(schedulerConfig.targets.length, 3); - final Target a = schedulerConfig.targets.first; - final Target b = schedulerConfig.targets[1]; - final Target c = schedulerConfig.targets[2]; - expect(a.name, 'A'); - expect(b.name, 'B'); - expect(b.dependencies, ['A']); - expect(c.name, 'C'); - expect(c.dependencies, ['B']); - }); - - test('constructs graph with parent with two dependents', () { - final YamlMap? twoDependentTargetConfig = loadYaml(''' -enabled_branches: - - master -targets: - - name: A - - name: B1 - dependencies: - - A - - name: B2 - dependencies: - - A - ''') as YamlMap?; - final SchedulerConfig unCheckedSchedulerConfig = SchedulerConfig()..mergeFromProto3Json(twoDependentTargetConfig); - final SchedulerConfig schedulerConfig = CiYaml( - slug: Config.flutterSlug, - branch: Config.defaultBranch(Config.flutterSlug), - config: unCheckedSchedulerConfig, - validate: true, - ).config; - expect(schedulerConfig.targets.length, 3); - final Target a = schedulerConfig.targets.first; - final Target b1 = schedulerConfig.targets[1]; - final Target b2 = schedulerConfig.targets[2]; - expect(a.name, 'A'); - expect(b1.name, 'B1'); - expect(b1.dependencies, ['A']); - expect(b2.name, 'B2'); - expect(b2.dependencies, ['A']); - }); - - test('fails when there are cyclic targets', () { - final YamlMap? configWithCycle = loadYaml(''' -enabled_branches: - - master -targets: - - name: A - dependencies: - - B - - name: B - dependencies: - - A - ''') as YamlMap?; - final SchedulerConfig unCheckedSchedulerConfig = SchedulerConfig()..mergeFromProto3Json(configWithCycle); - expect( - () => CiYaml( - slug: Config.flutterSlug, - branch: Config.defaultBranch(Config.flutterSlug), - config: unCheckedSchedulerConfig, - validate: true, - ).config, - throwsA( - isA().having( - (FormatException e) => e.toString(), - 'message', - contains('ERROR: A depends on B which does not exist'), - ), - ), - ); - }); - - test('fails when there are duplicate targets', () { - final YamlMap? configWithDuplicateTargets = loadYaml(''' -enabled_branches: - - master -targets: - - name: A - - name: A - ''') as YamlMap?; - final SchedulerConfig unCheckedSchedulerConfig = SchedulerConfig() - ..mergeFromProto3Json(configWithDuplicateTargets); - expect( - () => CiYaml( - slug: Config.flutterSlug, - branch: Config.defaultBranch(Config.flutterSlug), - config: unCheckedSchedulerConfig, - validate: true, - ).config, - throwsA( - isA().having( - (FormatException e) => e.toString(), - 'message', - contains('ERROR: A already exists in graph'), - ), - ), - ); - }); - - test('fails when there are multiple dependencies', () { - final YamlMap? configWithMultipleDependencies = loadYaml(''' -enabled_branches: - - master -targets: - - name: A - - name: B - - name: C - dependencies: - - A - - B - ''') as YamlMap?; - final SchedulerConfig unCheckedSchedulerConfig = SchedulerConfig() - ..mergeFromProto3Json(configWithMultipleDependencies); - expect( - () => CiYaml( - slug: Config.flutterSlug, - branch: Config.defaultBranch(Config.flutterSlug), - config: unCheckedSchedulerConfig, - validate: true, - ).config, - throwsA( - isA().having( - (FormatException e) => e.toString(), - 'message', - contains('ERROR: C has multiple dependencies which is not supported. Use only one dependency'), - ), - ), - ); - }); - - test('fails when dependency does not exist', () { - final YamlMap? configWithMissingTarget = loadYaml(''' -enabled_branches: - - master -targets: - - name: A - dependencies: - - B - ''') as YamlMap?; - final SchedulerConfig unCheckedSchedulerConfig = SchedulerConfig()..mergeFromProto3Json(configWithMissingTarget); - expect( - () => CiYaml( - slug: Config.flutterSlug, - branch: Config.defaultBranch(Config.flutterSlug), - config: unCheckedSchedulerConfig, - validate: true, - ).config, - throwsA( - isA().having( - (FormatException e) => e.toString(), - 'message', - contains('ERROR: A depends on B which does not exist'), - ), - ), - ); - }); - }); - - group('validate scheduler config and compared with tip of tree targets', () { - late CiYaml? totConfig; - - setUp(() { - final YamlMap? totYaml = loadYaml(''' -enabled_branches: - - master -targets: - - name: A - ''') as YamlMap?; - final SchedulerConfig unCheckedSchedulerConfig = SchedulerConfig()..mergeFromProto3Json(totYaml); - totConfig = CiYaml( - slug: Config.flutterSlug, - branch: Config.defaultBranch(Config.flutterSlug), - config: unCheckedSchedulerConfig, - validate: true, - ); - }); - - test('succeed when no new builders compared with tip of tree builders', () { - final YamlMap? currentYaml = loadYaml(''' -enabled_branches: - - master -targets: - - name: A - ''') as YamlMap?; - final SchedulerConfig unCheckedSchedulerConfig = SchedulerConfig()..mergeFromProto3Json(currentYaml); - expect( - () => CiYaml( - slug: Config.flutterSlug, - branch: Config.defaultBranch(Config.flutterSlug), - config: unCheckedSchedulerConfig, - totConfig: totConfig, - validate: true, - ), - returnsNormally, - ); - }); - - test('succeed when new builder is marked with bringup:true ', () { - final YamlMap? currentYaml = loadYaml(''' -enabled_branches: - - master -targets: - - name: A - - name: B - bringup: true - ''') as YamlMap?; - final SchedulerConfig unCheckedSchedulerConfig = SchedulerConfig()..mergeFromProto3Json(currentYaml); - expect( - () => CiYaml( - slug: Config.flutterSlug, - branch: Config.defaultBranch(Config.flutterSlug), - config: unCheckedSchedulerConfig, - totConfig: totConfig, - validate: true, - ), - returnsNormally, - ); - }); - - test('fails when new builder is missing bringup:true ', () { - final YamlMap? currentYaml = loadYaml(''' -enabled_branches: - - master -targets: - - name: A - - name: B - ''') as YamlMap?; - final SchedulerConfig unCheckedSchedulerConfig = SchedulerConfig()..mergeFromProto3Json(currentYaml); - expect( - () => CiYaml( - slug: Config.flutterSlug, - branch: Config.defaultBranch(Config.flutterSlug), - config: unCheckedSchedulerConfig, - totConfig: totConfig, - validate: true, - ), - throwsA( - isA().having( - (FormatException e) => e.toString(), - 'message', - contains('ERROR: B is a new builder added. it needs to be marked bringup: true'), - ), - ), - ); - }); - - test('fails when new builder has bringup set to false ', () { - final YamlMap? currentYaml = loadYaml(''' -enabled_branches: - - master -targets: - - name: A - - name: B - bringup: false - ''') as YamlMap?; - final SchedulerConfig unCheckedSchedulerConfig = SchedulerConfig()..mergeFromProto3Json(currentYaml); - expect( - () => CiYaml( - slug: Config.flutterSlug, - branch: Config.defaultBranch(Config.flutterSlug), - config: unCheckedSchedulerConfig, - totConfig: totConfig, - validate: true, - ), - throwsA( - isA().having( - (FormatException e) => e.toString(), - 'message', - contains('ERROR: B is a new builder added. it needs to be marked bringup: true'), - ), - ), - ); - }); - }); -} diff --git a/app_dart/test/service/scheduler/policy_test.dart b/app_dart/test/service/scheduler/policy_test.dart deleted file mode 100644 index 44006406a..000000000 --- a/app_dart/test/service/scheduler/policy_test.dart +++ /dev/null @@ -1,157 +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:cocoon_service/src/model/appengine/task.dart'; -import 'package:cocoon_service/src/service/datastore.dart'; -import 'package:cocoon_service/src/service/luci_build_service.dart'; -import 'package:cocoon_service/src/service/scheduler/policy.dart'; -import 'package:test/test.dart'; - -import '../../src/datastore/fake_datastore.dart'; -import '../../src/utilities/entity_generators.dart'; - -void main() { - group('BatchPolicy', () { - late FakeDatastoreDB db; - late DatastoreService datastore; - - final BatchPolicy policy = BatchPolicy(); - - setUp(() { - db = FakeDatastoreDB(); - datastore = DatastoreService(db, 5); - }); - - final List allPending = [ - generateTask(3), - generateTask(2), - generateTask(1), - ]; - - final List latestAllPending = [ - generateTask(6), - generateTask(5), - generateTask(4), - generateTask(3), - generateTask(2), - generateTask(1, status: Task.statusSucceeded), - ]; - - final List latestFinishedButRestPending = [ - generateTask(6, status: Task.statusSucceeded), - generateTask(5), - generateTask(4), - generateTask(3), - generateTask(2), - generateTask(1), - ]; - - final List latestFailed = [ - generateTask(6, status: Task.statusFailed), - generateTask(5), - generateTask(4), - generateTask(3), - generateTask(2), - generateTask(1), - ]; - - final List latestPending = [ - generateTask(6), - generateTask(5), - generateTask(4), - generateTask(3), - generateTask(2, status: Task.statusSucceeded), - generateTask(1, status: Task.statusSucceeded), - ]; - - final List failedWithRunning = [ - generateTask(6), - generateTask(5), - generateTask(4), - generateTask(3, status: Task.statusFailed), - generateTask(2, status: Task.statusInProgress), - generateTask(1), - ]; - - test('triggers if less tasks than batch size', () async { - db.addOnQuery((Iterable results) => allPending); - expect( - await policy.triggerPriority(task: generateTask(4), datastore: datastore), - null, - ); - }); - - test('triggers after batch size', () async { - db.addOnQuery((Iterable results) => latestAllPending); - expect( - await policy.triggerPriority(task: generateTask(7), datastore: datastore), - LuciBuildService.kDefaultPriority, - ); - }); - - test('triggers with higher priority on recent failures', () async { - db.addOnQuery((Iterable results) => latestFailed); - expect( - await policy.triggerPriority(task: generateTask(7), datastore: datastore), - LuciBuildService.kRerunPriority, - ); - }); - - test('does not trigger on recent failures if there is already a running task', () async { - db.addOnQuery((Iterable results) => failedWithRunning); - expect( - await policy.triggerPriority(task: generateTask(7), datastore: datastore), - isNull, - ); - }); - - test('does not trigger when a test was recently scheduled', () async { - db.addOnQuery((Iterable results) => latestFinishedButRestPending); - expect(await policy.triggerPriority(task: generateTask(7), datastore: datastore), isNull); - }); - - test('does not trigger when pending queue is smaller than batch', () async { - db.addOnQuery((Iterable results) => latestPending); - expect(await policy.triggerPriority(task: generateTask(7), datastore: datastore), isNull); - }); - - test('do not return rerun priority when tasks length is smaller than batch size', () { - expect(shouldRerunPriority(allPending, 5), false); - }); - }); - - group('GuaranteedPolicy', () { - late FakeDatastoreDB db; - late DatastoreService datastore; - - final GuaranteedPolicy policy = GuaranteedPolicy(); - - setUp(() { - db = FakeDatastoreDB(); - datastore = DatastoreService(db, 5); - }); - - final List pending = [ - generateTask(1), - ]; - - final List latestFailed = [generateTask(1, status: Task.statusFailed)]; - - test('triggers every task', () async { - db.addOnQuery((Iterable results) => pending); - expect( - await policy.triggerPriority(task: generateTask(2), datastore: datastore), - LuciBuildService.kDefaultPriority, - ); - }); - - test('triggers with higher priority on recent failure', () async { - db.addOnQuery((Iterable results) => latestFailed); - expect( - await policy.triggerPriority(task: generateTask(2), datastore: datastore), - LuciBuildService.kRerunPriority, - ); - }); - }); -} diff --git a/app_dart/test/service/scheduler_test.dart b/app_dart/test/service/scheduler_test.dart deleted file mode 100644 index 0d9426630..000000000 --- a/app_dart/test/service/scheduler_test.dart +++ /dev/null @@ -1,1181 +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 'dart:io'; - -import 'package:cocoon_service/src/foundation/utils.dart'; -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:cocoon_service/src/model/ci_yaml/target.dart'; -import 'package:cocoon_service/src/model/github/checks.dart' as cocoon_checks; -import 'package:cocoon_service/src/model/luci/buildbucket.dart'; -import 'package:cocoon_service/src/service/build_status_provider.dart'; -import 'package:cocoon_service/src/service/cache_service.dart'; -import 'package:cocoon_service/src/service/config.dart'; -import 'package:cocoon_service/src/service/datastore.dart'; -import 'package:cocoon_service/src/service/github_checks_service.dart'; -import 'package:cocoon_service/src/service/scheduler.dart'; -import 'package:gcloud/db.dart' as gcloud_db; -import 'package:gcloud/db.dart'; -import 'package:github/github.dart'; -import 'package:github/hooks.dart'; -import 'package:googleapis/bigquery/v2.dart'; -import 'package:http/http.dart' as http; -import 'package:http/testing.dart'; -import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; - -import '../model/github/checks_test_data.dart'; -import '../src/datastore/fake_config.dart'; -import '../src/datastore/fake_datastore.dart'; -import '../src/service/fake_build_status_provider.dart'; -import '../src/request_handling/fake_pubsub.dart'; -import '../src/service/fake_buildbucket.dart'; -import '../src/service/fake_gerrit_service.dart'; -import '../src/service/fake_github_service.dart'; -import '../src/service/fake_luci_build_service.dart'; -import '../src/utilities/entity_generators.dart'; -import '../src/utilities/mocks.dart'; - -const String singleCiYaml = r''' -enabled_branches: - - master - - main - - flutter-\d+\.\d+-candidate\.\d+ -targets: - - name: Linux A - properties: - custom: abc - - name: Linux B - enabled_branches: - - stable - scheduler: luci - - name: Linux runIf - runIf: - - dev/** - - name: Google Internal Roll - postsubmit: true - presubmit: false - scheduler: google_internal -'''; - -void main() { - late CacheService cache; - late FakeConfig config; - late FakeDatastoreDB db; - late FakeBuildStatusService buildStatusService; - late MockClient httpClient; - late MockGithubChecksUtil mockGithubChecksUtil; - late Scheduler scheduler; - - final PullRequest pullRequest = generatePullRequest(id: 42); - - Commit shaToCommit(String sha, {String branch = 'master'}) { - return Commit( - key: db.emptyKey.append(Commit, id: 'flutter/flutter/$branch/$sha'), - repository: 'flutter/flutter', - sha: sha, - branch: branch, - timestamp: int.parse(sha), - ); - } - - group('Scheduler', () { - setUp(() { - final MockTabledataResource tabledataResource = MockTabledataResource(); - when(tabledataResource.insertAll(any, any, any, any)).thenAnswer((_) async { - return TableDataInsertAllResponse(); - }); - - cache = CacheService(inMemory: true); - db = FakeDatastoreDB(); - buildStatusService = FakeBuildStatusService( - commitStatuses: [ - CommitStatus(generateCommit(1), const []), - CommitStatus(generateCommit(1, branch: 'main', repo: Config.engineSlug.name), const []), - ], - ); - config = FakeConfig( - tabledataResource: tabledataResource, - dbValue: db, - githubService: FakeGithubService(), - githubClient: MockGitHub(), - supportedReposValue: { - Config.engineSlug, - Config.flutterSlug, - }, - ); - httpClient = MockClient((http.Request request) async { - if (request.url.path.contains('.ci.yaml')) { - return http.Response(singleCiYaml, 200); - } - throw Exception('Failed to find ${request.url.path}'); - }); - - mockGithubChecksUtil = MockGithubChecksUtil(); - // Generate check runs based on the name hash code - when(mockGithubChecksUtil.createCheckRun(any, any, any, any, output: anyNamed('output'))) - .thenAnswer((Invocation invocation) async => generateCheckRun(invocation.positionalArguments[2].hashCode)); - scheduler = Scheduler( - cache: cache, - config: config, - datastoreProvider: (DatastoreDB db) => DatastoreService(db, 2), - buildStatusProvider: (_) => buildStatusService, - githubChecksService: GithubChecksService(config, githubChecksUtil: mockGithubChecksUtil), - httpClientProvider: () => httpClient, - luciBuildService: FakeLuciBuildService( - config: config, - githubChecksUtil: mockGithubChecksUtil, - gerritService: FakeGerritService( - branchesValue: ['master', 'main'], - ), - ), - ); - - when(mockGithubChecksUtil.createCheckRun(any, any, any, any)).thenAnswer((_) async { - return CheckRun.fromJson(const { - 'id': 1, - 'started_at': '2020-05-10T02:49:31Z', - 'check_suite': {'id': 2}, - }); - }); - }); - - group('add commits', () { - final FakePubSub pubsub = FakePubSub(); - List createCommitList( - List shas, { - String repo = 'flutter', - String branch = 'master', - }) { - return List.generate( - shas.length, - (int index) => Commit( - author: 'Username', - authorAvatarUrl: 'http://example.org/avatar.jpg', - branch: branch, - key: db.emptyKey.append(Commit, id: 'flutter/$repo/$branch/${shas[index]}'), - message: 'commit message', - repository: 'flutter/$repo', - sha: shas[index], - timestamp: DateTime.fromMillisecondsSinceEpoch(int.parse(shas[index])).millisecondsSinceEpoch, - ), - ); - } - - test('succeeds when GitHub returns no commits', () async { - await scheduler.addCommits([]); - expect(db.values, isEmpty); - }); - - test('inserts all relevant fields of the commit', () async { - config.supportedBranchesValue = ['master']; - expect(db.values.values.whereType().length, 0); - await scheduler.addCommits(createCommitList(['1'])); - expect(db.values.values.whereType().length, 1); - final Commit commit = db.values.values.whereType().single; - expect(commit.repository, 'flutter/flutter'); - expect(commit.branch, 'master'); - expect(commit.sha, '1'); - expect(commit.timestamp, 1); - expect(commit.author, 'Username'); - expect(commit.authorAvatarUrl, 'http://example.org/avatar.jpg'); - expect(commit.message, 'commit message'); - }); - - test('skips scheduling for unsupported repos', () async { - config.supportedBranchesValue = ['master']; - await scheduler.addCommits(createCommitList(['1'], repo: 'not-supported')); - expect(db.values.values.whereType().length, 0); - }); - - test('skips commits for which transaction commit fails', () async { - config.supportedBranchesValue = ['master']; - - // Existing commits should not be duplicated. - final Commit commit = shaToCommit('1'); - db.values[commit.key] = commit; - - db.onCommit = (List> inserts, List> deletes) { - if (inserts.whereType().where((Commit commit) => commit.sha == '3').isNotEmpty) { - throw StateError('Commit failed'); - } - }; - // Commits are expect from newest to oldest timestamps - await scheduler.addCommits(createCommitList(['2', '3', '4'])); - expect(db.values.values.whereType().length, 3); - // The 2 new commits are scheduled 3 tasks, existing commit has none. - expect(db.values.values.whereType().length, 2 * 3); - // Check commits were added, but 3 was not - expect(db.values.values.whereType().map(toSha), containsAll(['1', '2', '4'])); - expect(db.values.values.whereType().map(toSha), isNot(contains('3'))); - }); - - test('skips commits for which task transaction fails', () async { - config.supportedBranchesValue = ['master']; - - // Existing commits should not be duplicated. - final Commit commit = shaToCommit('1'); - db.values[commit.key] = commit; - - db.onCommit = (List> inserts, List> deletes) { - if (inserts.whereType().where((Task task) => task.createTimestamp == 3).isNotEmpty) { - throw StateError('Task failed'); - } - }; - // Commits are expect from newest to oldest timestamps - await scheduler.addCommits(createCommitList(['2', '3', '4'])); - expect(db.values.values.whereType().length, 3); - // The 2 new commits are scheduled 3 tasks, existing commit has none. - expect(db.values.values.whereType().length, 2 * 3); - // Check commits were added, but 3 was not - expect(db.values.values.whereType().map(toSha), containsAll(['1', '2', '4'])); - expect(db.values.values.whereType().map(toSha), isNot(contains('3'))); - }); - - test('schedules cocoon based targets', () async { - final MockLuciBuildService luciBuildService = MockLuciBuildService(); - when( - luciBuildService.schedulePostsubmitBuilds( - commit: anyNamed('commit'), - toBeScheduled: captureAnyNamed('toBeScheduled'), - ), - ).thenAnswer((_) => Future>>.value(>[])); - buildStatusService = FakeBuildStatusService( - commitStatuses: [ - CommitStatus(generateCommit(1, repo: 'engine', branch: 'main'), const []), - ], - ); - scheduler = Scheduler( - cache: cache, - config: config, - buildStatusProvider: (_) => buildStatusService, - datastoreProvider: (DatastoreDB db) => DatastoreService(db, 2), - githubChecksService: GithubChecksService(config, githubChecksUtil: mockGithubChecksUtil), - httpClientProvider: () => httpClient, - luciBuildService: luciBuildService, - ); - - await scheduler.addCommits(createCommitList(['1'], repo: 'engine', branch: 'main')); - final List captured = verify( - luciBuildService.schedulePostsubmitBuilds( - commit: anyNamed('commit'), - toBeScheduled: captureAnyNamed('toBeScheduled'), - ), - ).captured; - final List toBeScheduled = captured.first as List; - expect(toBeScheduled.length, 2); - final Iterable> tuples = - toBeScheduled.map((dynamic tuple) => tuple as Tuple); - final Iterable scheduledTargetNames = - tuples.map((Tuple tuple) => tuple.second.name!); - expect(scheduledTargetNames, ['Linux A', 'Linux runIf']); - // Tasks triggered by cocoon are marked as in progress - final Iterable tasks = db.values.values.whereType(); - expect(tasks.singleWhere((Task task) => task.name == 'Linux A').status, Task.statusInProgress); - }); - - test('schedules cocoon based targets - multiple batch requests', () async { - final MockBuildBucketClient mockBuildBucketClient = MockBuildBucketClient(); - final FakeLuciBuildService luciBuildService = FakeLuciBuildService( - config: config, - buildbucket: mockBuildBucketClient, - gerritService: FakeGerritService(), - githubChecksUtil: mockGithubChecksUtil, - pubsub: pubsub, - ); - when(mockGithubChecksUtil.createCheckRun(any, any, any, any, output: anyNamed('output'))) - .thenAnswer((_) async => generateCheckRun(1, name: 'Linux A')); - when(mockBuildBucketClient.listBuilders(any)).thenAnswer((_) async { - return const ListBuildersResponse( - builders: [ - BuilderItem(id: BuilderId(bucket: 'prod', project: 'flutter', builder: 'Linux A')), - BuilderItem(id: BuilderId(bucket: 'prod', project: 'flutter', builder: 'Linux runIf')), - ], - ); - }); - buildStatusService = FakeBuildStatusService( - commitStatuses: [ - CommitStatus(generateCommit(1, repo: 'engine', branch: 'main'), const []), - ], - ); - config.batchSizeValue = 1; - scheduler = Scheduler( - cache: cache, - config: config, - buildStatusProvider: (_) => buildStatusService, - datastoreProvider: (DatastoreDB db) => DatastoreService(db, 2), - githubChecksService: GithubChecksService(config, githubChecksUtil: mockGithubChecksUtil), - httpClientProvider: () => httpClient, - luciBuildService: luciBuildService, - ); - - await scheduler.addCommits(createCommitList(['1'], repo: 'engine', branch: 'main')); - expect(pubsub.messages.length, 2); - }); - }); - - group('add pull request', () { - test('creates expected commit', () async { - final PullRequest mergedPr = generatePullRequest(); - await scheduler.addPullRequest(mergedPr); - - expect(db.values.values.whereType().length, 1); - final Commit commit = db.values.values.whereType().single; - expect(commit.repository, 'flutter/flutter'); - expect(commit.branch, 'master'); - expect(commit.sha, 'abc'); - expect(commit.timestamp, 1); - expect(commit.author, 'dash'); - expect(commit.authorAvatarUrl, 'dashatar'); - expect(commit.message, 'example message'); - }); - - test('schedules tasks against merged PRs', () async { - final PullRequest mergedPr = generatePullRequest(); - await scheduler.addPullRequest(mergedPr); - - expect(db.values.values.whereType().length, 1); - expect(db.values.values.whereType().length, 3); - }); - - test('guarantees scheduling of tasks against merged release branch PR', () async { - final PullRequest mergedPr = generatePullRequest(branch: 'flutter-3.2-candidate.5'); - await scheduler.addPullRequest(mergedPr); - - expect(db.values.values.whereType().length, 1); - expect(db.values.values.whereType().length, 3); - // Ensure all tasks have been marked in progress - expect(db.values.values.whereType().where((Task task) => task.status == Task.statusNew), isEmpty); - }); - - test('guarantees scheduling of tasks against merged engine PR', () async { - final PullRequest mergedPr = generatePullRequest( - repo: Config.engineSlug.name, - branch: Config.defaultBranch(Config.engineSlug), - ); - await scheduler.addPullRequest(mergedPr); - - expect(db.values.values.whereType().length, 1); - expect(db.values.values.whereType().length, 3); - // Ensure all tasks under cocoon scheduler have been marked in progress - expect(db.values.values.whereType().where((Task task) => task.status == Task.statusInProgress).length, 2); - }); - - test('Release candidate branch commit filters builders not in default branch', () async { - const String totCiYaml = r''' -enabled_branches: - - main - - flutter-\d+\.\d+-candidate\.\d+ -targets: - - name: Linux A - properties: - custom: abc -'''; - httpClient = MockClient((http.Request request) async { - if (request.url.path == '/flutter/engine/abc/.ci.yaml') { - return http.Response(totCiYaml, HttpStatus.ok); - } - if (request.url.path == '/flutter/engine/1/.ci.yaml') { - return http.Response(singleCiYaml, HttpStatus.ok); - } - print(request.url.path); - throw Exception('Failed to find ${request.url.path}'); - }); - scheduler = Scheduler( - cache: cache, - config: config, - datastoreProvider: (DatastoreDB db) => DatastoreService(db, 2), - buildStatusProvider: (_) => buildStatusService, - githubChecksService: GithubChecksService(config, githubChecksUtil: mockGithubChecksUtil), - httpClientProvider: () => httpClient, - luciBuildService: FakeLuciBuildService( - config: config, - githubChecksUtil: mockGithubChecksUtil, - gerritService: FakeGerritService( - branchesValue: ['master', 'main'], - ), - ), - ); - - final PullRequest mergedPr = generatePullRequest( - repo: Config.engineSlug.name, - branch: 'flutter-3.10-candidate.1', - ); - await scheduler.addPullRequest(mergedPr); - - final List tasks = db.values.values.whereType().toList(); - expect(db.values.values.whereType().length, 1); - expect(tasks, hasLength(1)); - expect(tasks.first.name, 'Linux A'); - // Ensure all tasks under cocoon scheduler have been marked in progress - expect(db.values.values.whereType().where((Task task) => task.status == Task.statusInProgress).length, 1); - }); - - test('does not schedule tasks against non-merged PRs', () async { - final PullRequest notMergedPr = generatePullRequest(merged: false); - await scheduler.addPullRequest(notMergedPr); - - expect(db.values.values.whereType().map(toSha).length, 0); - expect(db.values.values.whereType().length, 0); - }); - - test('does not schedule tasks against already added PRs', () async { - // Existing commits should not be duplicated. - final Commit commit = shaToCommit('1'); - db.values[commit.key] = commit; - - final PullRequest alreadyLandedPr = generatePullRequest(sha: '1'); - await scheduler.addPullRequest(alreadyLandedPr); - - expect(db.values.values.whereType().map(toSha).length, 1); - // No tasks should be scheduled as that is done on commit insert. - expect(db.values.values.whereType().length, 0); - }); - - test('creates expected commit from release branch PR', () async { - final PullRequest mergedPr = generatePullRequest(branch: '1.26'); - await scheduler.addPullRequest(mergedPr); - - expect(db.values.values.whereType().length, 1); - final Commit commit = db.values.values.whereType().single; - expect(commit.repository, 'flutter/flutter'); - expect(commit.branch, '1.26'); - expect(commit.sha, 'abc'); - expect(commit.timestamp, 1); - expect(commit.author, 'dash'); - expect(commit.authorAvatarUrl, 'dashatar'); - expect(commit.message, 'example message'); - }); - }); - - group('process check run', () { - test('rerequested ci.yaml check retriggers presubmit', () async { - final MockGithubService mockGithubService = MockGithubService(); - final MockGitHub mockGithubClient = MockGitHub(); - buildStatusService = - FakeBuildStatusService(commitStatuses: [CommitStatus(generateCommit(1), const [])]); - config = FakeConfig( - githubService: mockGithubService, - ); - scheduler = Scheduler( - cache: cache, - config: config, - buildStatusProvider: (_) => buildStatusService, - githubChecksService: GithubChecksService(config, githubChecksUtil: mockGithubChecksUtil), - httpClientProvider: () => httpClient, - luciBuildService: FakeLuciBuildService( - config: config, - githubChecksUtil: mockGithubChecksUtil, - ), - ); - when(mockGithubService.github).thenReturn(mockGithubClient); - when(mockGithubService.searchIssuesAndPRs(any, any, sort: anyNamed('sort'), pages: anyNamed('pages'))) - .thenAnswer((_) async => [generateIssue(3)]); - when(mockGithubChecksUtil.listCheckSuitesForRef(any, any, ref: anyNamed('ref'))).thenAnswer( - (_) async => [ - // From check_run.check_suite.id in [checkRunString]. - generateCheckSuite(668083231), - ], - ); - when(mockGithubService.getPullRequest(any, any)).thenAnswer((_) async => generatePullRequest()); - when(mockGithubService.listFiles(any)).thenAnswer((_) async => ['abc/def']); - when( - mockGithubChecksUtil.createCheckRun( - any, - any, - any, - any, - output: anyNamed('output'), - ), - ).thenAnswer((_) async { - return CheckRun.fromJson(const { - 'id': 1, - 'started_at': '2020-05-10T02:49:31Z', - 'name': Scheduler.kCiYamlCheckName, - 'check_suite': {'id': 2}, - }); - }); - final Map checkRunEventJson = jsonDecode(checkRunString) as Map; - checkRunEventJson['check_run']['name'] = Scheduler.kCiYamlCheckName; - final cocoon_checks.CheckRunEvent checkRunEvent = cocoon_checks.CheckRunEvent.fromJson(checkRunEventJson); - expect(await scheduler.processCheckRun(checkRunEvent), true); - verify( - mockGithubChecksUtil.createCheckRun( - any, - any, - any, - Scheduler.kCiYamlCheckName, - output: anyNamed('output'), - ), - ); - // Verfies Linux A was created - verify(mockGithubChecksUtil.createCheckRun(any, any, any, any)).called(1); - }); - - test('rerequested presubmit check triggers presubmit build', () async { - // Note that we're not inserting any commits into the db, because - // only postsubmit commits are stored in the datastore. - config = FakeConfig(dbValue: db); - db = FakeDatastoreDB(); - - // Set up mock buildbucket to validate which bucket is requested. - final MockBuildBucketClient mockBuildbucket = MockBuildBucketClient(); - when(mockBuildbucket.batch(any)).thenAnswer((i) async { - return FakeBuildBucketClient().batch(i.positionalArguments[0]); - }); - when(mockBuildbucket.scheduleBuild(any, buildBucketUri: anyNamed('buildBucketUri'))) - .thenAnswer((realInvocation) async { - final ScheduleBuildRequest scheduleBuildRequest = realInvocation.positionalArguments[0]; - // Ensure this is an attempt to schedule a presubmit build by - // verifying that bucket == 'try'. - expect(scheduleBuildRequest.builderId.bucket, equals('try')); - return const Build(builderId: BuilderId(), id: ''); - }); - - scheduler = Scheduler( - cache: cache, - config: config, - githubChecksService: GithubChecksService(config, githubChecksUtil: mockGithubChecksUtil), - luciBuildService: FakeLuciBuildService( - config: config, - githubChecksUtil: mockGithubChecksUtil, - buildbucket: mockBuildbucket, - ), - ); - final cocoon_checks.CheckRunEvent checkRunEvent = cocoon_checks.CheckRunEvent.fromJson( - jsonDecode(checkRunString) as Map, - ); - expect(await scheduler.processCheckRun(checkRunEvent), true); - verify(mockBuildbucket.scheduleBuild(any, buildBucketUri: anyNamed('buildBucketUri'))).called(1); - verify(mockGithubChecksUtil.createCheckRun(any, any, any, any)).called(1); - }); - - test('rerequested postsubmit check triggers postsubmit build', () async { - // Set up datastore with postsubmit entities matching [checkRunString]. - db = FakeDatastoreDB(); - config = FakeConfig(dbValue: db, postsubmitSupportedReposValue: {RepositorySlug('abc', 'cocoon')}); - final Commit commit = generateCommit( - 1, - sha: '66d6bd9a3f79a36fe4f5178ccefbc781488a596c', - branch: 'independent_agent', - owner: 'abc', - repo: 'cocoon', - ); - final Commit commitToT = generateCommit( - 1, - sha: '66d6bd9a3f79a36fe4f5178ccefbc781488a592c', - branch: 'master', - owner: 'abc', - repo: 'cocoon', - ); - config.db.values[commit.key] = commit; - config.db.values[commitToT.key] = commitToT; - final Task task = generateTask(1, name: 'test1', parent: commit); - config.db.values[task.key] = task; - - // Set up ci.yaml with task name and branch name from [checkRunString]. - httpClient = MockClient((http.Request request) async { - if (request.url.path.contains('.ci.yaml')) { - return http.Response( - r''' -enabled_branches: - - independent_agent - - master -targets: - - name: test1 -''', - 200, - ); - } - throw Exception('Failed to find ${request.url.path}'); - }); - - // Set up mock buildbucket to validate which bucket is requested. - final MockBuildBucketClient mockBuildbucket = MockBuildBucketClient(); - when(mockBuildbucket.batch(any)).thenAnswer((i) async { - return FakeBuildBucketClient().batch(i.positionalArguments[0]); - }); - when(mockBuildbucket.scheduleBuild(any, buildBucketUri: anyNamed('buildBucketUri'))) - .thenAnswer((realInvocation) async { - final ScheduleBuildRequest scheduleBuildRequest = realInvocation.positionalArguments[0]; - // Ensure this is an attempt to schedule a postsubmit build by - // verifying that bucket == 'prod'. - expect(scheduleBuildRequest.builderId.bucket, equals('prod')); - return const Build(builderId: BuilderId(), id: ''); - }); - - scheduler = Scheduler( - cache: cache, - config: config, - githubChecksService: GithubChecksService(config, githubChecksUtil: mockGithubChecksUtil), - httpClientProvider: () => httpClient, - luciBuildService: FakeLuciBuildService( - config: config, - githubChecksUtil: mockGithubChecksUtil, - buildbucket: mockBuildbucket, - gerritService: FakeGerritService( - branchesValue: ['master', 'main'], - ), - ), - ); - final cocoon_checks.CheckRunEvent checkRunEvent = cocoon_checks.CheckRunEvent.fromJson( - jsonDecode(checkRunString) as Map, - ); - expect(await scheduler.processCheckRun(checkRunEvent), true); - verify(mockBuildbucket.scheduleBuild(any, buildBucketUri: anyNamed('buildBucketUri'))).called(1); - verify(mockGithubChecksUtil.createCheckRun(any, any, any, any)).called(1); - }); - - test('rerequested does not fail on empty pull request list', () async { - when(mockGithubChecksUtil.createCheckRun(any, any, any, any)).thenAnswer((_) async { - return CheckRun.fromJson(const { - 'id': 1, - 'started_at': '2020-05-10T02:49:31Z', - 'check_suite': {'id': 2}, - }); - }); - final cocoon_checks.CheckRunEvent checkRunEvent = cocoon_checks.CheckRunEvent.fromJson( - jsonDecode(checkRunWithEmptyPullRequests) as Map, - ); - expect(await scheduler.processCheckRun(checkRunEvent), true); - verify(mockGithubChecksUtil.createCheckRun(any, any, any, any)).called(1); - }); - }); - - group('presubmit', () { - test('gets only enabled .ci.yaml builds', () async { - httpClient = MockClient((http.Request request) async { - if (request.url.path.contains('.ci.yaml')) { - return http.Response( - ''' -enabled_branches: - - master -targets: - - name: Linux A - presubmit: true - scheduler: luci - - name: Linux B - scheduler: luci - enabled_branches: - - stable - presubmit: true - - name: Linux C - scheduler: luci - enabled_branches: - - master - presubmit: true - - name: Linux D - scheduler: luci - bringup: true - presubmit: true - - name: Google-internal roll - scheduler: google_internal - enabled_branches: - - master - presubmit: true - ''', - 200, - ); - } - throw Exception('Failed to find ${request.url.path}'); - }); - final List presubmitTargets = await scheduler.getPresubmitTargets(pullRequest); - expect( - presubmitTargets.map((Target target) => target.value.name).toList(), - containsAll(['Linux A', 'Linux C']), - ); - }); - - test('checks for release branches', () async { - const String branch = 'flutter-1.24-candidate.1'; - httpClient = MockClient((http.Request request) async { - if (request.url.path.contains('.ci.yaml')) { - return http.Response( - ''' -enabled_branches: - - master -targets: - - name: Linux A - presubmit: true - scheduler: luci - ''', - 200, - ); - } - throw Exception('Failed to find ${request.url.path}'); - }); - expect( - scheduler.getPresubmitTargets(generatePullRequest(branch: branch)), - throwsA(predicate((Exception e) => e.toString().contains('$branch is not enabled'))), - ); - }); - - test('checks for release branch regex', () async { - const String branch = 'flutter-1.24-candidate.1'; - httpClient = MockClient((http.Request request) async { - if (request.url.path.contains('.ci.yaml')) { - return http.Response( - ''' -enabled_branches: - - main - - master - - flutter-\\d+.\\d+-candidate.\\d+ -targets: - - name: Linux A - scheduler: luci - ''', - 200, - ); - } - throw Exception('Failed to find ${request.url.path}'); - }); - final List targets = await scheduler.getPresubmitTargets(generatePullRequest(branch: branch)); - expect(targets.single.value.name, 'Linux A'); - }); - - test('triggers expected presubmit build checks', () async { - await scheduler.triggerPresubmitTargets(pullRequest: pullRequest); - expect( - verify(mockGithubChecksUtil.createCheckRun(any, any, any, captureAny, output: captureAnyNamed('output'))) - .captured, - [ - Scheduler.kCiYamlCheckName, - const CheckRunOutput( - title: Scheduler.kCiYamlCheckName, - summary: 'If this check is stuck pending, push an empty commit to retrigger the checks', - ), - 'Linux A', - null, - // Linux runIf is not run as this is for tip of tree and the files weren't affected - ], - ); - }); - - test('Do not schedule other targets on revert request.', () async { - final PullRequest releasePullRequest = generatePullRequest( - labels: [IssueLabel(name: 'revert of')], - ); - - releasePullRequest.user = User(login: 'auto-submit[bot]'); - - await scheduler.triggerPresubmitTargets(pullRequest: releasePullRequest); - expect( - verify(mockGithubChecksUtil.createCheckRun(any, any, any, captureAny, output: captureAnyNamed('output'))) - .captured, - [ - Scheduler.kCiYamlCheckName, - // No other targets should be created. - const CheckRunOutput( - title: Scheduler.kCiYamlCheckName, - summary: 'If this check is stuck pending, push an empty commit to retrigger the checks', - ), - ], - ); - }); - - test('filters out presubmit targets that do not exist in main and do not filter targets not in main', () async { - const String singleCiYaml = r''' -enabled_branches: - - master - - main - - flutter-\d+\.\d+-candidate\.\d+ -targets: - - name: Linux A - properties: - custom: abc - - name: Linux B - enabled_branches: - - flutter-\d+\.\d+-candidate\.\d+ - scheduler: luci - - name: Linux C - enabled_branches: - - main - - flutter-\d+\.\d+-candidate\.\d+ - scheduler: luci -'''; - const String totCiYaml = r''' -enabled_branches: - - main - - flutter-\d+\.\d+-candidate\.\d+ -targets: - - name: Linux A - bringup: true - properties: - custom: abc -'''; - httpClient = MockClient((http.Request request) async { - if (request.url.path == '/flutter/engine/1/.ci.yaml') { - return http.Response(totCiYaml, HttpStatus.ok); - } - if (request.url.path == '/flutter/engine/abc/.ci.yaml') { - return http.Response(singleCiYaml, HttpStatus.ok); - } - print(request.url.path); - throw Exception('Failed to find ${request.url.path}'); - }); - scheduler = Scheduler( - cache: cache, - config: config, - datastoreProvider: (DatastoreDB db) => DatastoreService(db, 2), - buildStatusProvider: (_) => buildStatusService, - githubChecksService: GithubChecksService(config, githubChecksUtil: mockGithubChecksUtil), - httpClientProvider: () => httpClient, - luciBuildService: FakeLuciBuildService( - config: config, - githubChecksUtil: mockGithubChecksUtil, - gerritService: FakeGerritService( - branchesValue: ['master', 'main'], - ), - ), - ); - final PullRequest pr = generatePullRequest( - repo: Config.engineSlug.name, - branch: 'flutter-3.10-candidate.1', - ); - final List targets = await scheduler.getPresubmitTargets(pr); - expect( - targets.map((Target target) => target.value.name).toList(), - containsAll(['Linux A', 'Linux B']), - ); - }); - - test('triggers all presubmit build checks when diff cannot be found', () async { - final MockGithubService mockGithubService = MockGithubService(); - when(mockGithubService.listFiles(pullRequest)) - .thenThrow(GitHubError(GitHub(), 'Requested Resource was Not Found')); - buildStatusService = - FakeBuildStatusService(commitStatuses: [CommitStatus(generateCommit(1), const [])]); - scheduler = Scheduler( - cache: cache, - config: FakeConfig( - // tabledataResource: tabledataResource, - dbValue: db, - githubService: mockGithubService, - githubClient: MockGitHub(), - ), - buildStatusProvider: (_) => buildStatusService, - datastoreProvider: (DatastoreDB db) => DatastoreService(db, 2), - githubChecksService: GithubChecksService(config, githubChecksUtil: mockGithubChecksUtil), - httpClientProvider: () => httpClient, - luciBuildService: FakeLuciBuildService( - config: config, - githubChecksUtil: mockGithubChecksUtil, - gerritService: FakeGerritService(branchesValue: ['master']), - ), - ); - await scheduler.triggerPresubmitTargets(pullRequest: pullRequest); - expect( - verify(mockGithubChecksUtil.createCheckRun(any, any, any, captureAny, output: captureAnyNamed('output'))) - .captured, - [ - Scheduler.kCiYamlCheckName, - const CheckRunOutput( - title: Scheduler.kCiYamlCheckName, - summary: 'If this check is stuck pending, push an empty commit to retrigger the checks', - ), - 'Linux A', - null, - // runIf requires a diff in dev, so an error will cause it to be triggered - 'Linux runIf', - null, - ], - ); - }); - - test('triggers all presubmit targets on release branch pull request', () async { - final PullRequest releasePullRequest = generatePullRequest( - branch: 'flutter-1.24-candidate.1', - ); - await scheduler.triggerPresubmitTargets(pullRequest: releasePullRequest); - expect( - verify(mockGithubChecksUtil.createCheckRun(any, any, any, captureAny, output: captureAnyNamed('output'))) - .captured, - [ - Scheduler.kCiYamlCheckName, - const CheckRunOutput( - title: Scheduler.kCiYamlCheckName, - summary: 'If this check is stuck pending, push an empty commit to retrigger the checks', - ), - 'Linux A', - null, - 'Linux runIf', - null, - ], - ); - }); - - test('ci.yaml validation passes with default config', () async { - when(mockGithubChecksUtil.getCheckRun(any, any, any)) - .thenAnswer((Invocation invocation) async => createCheckRun(id: 0)); - await scheduler.triggerPresubmitTargets(pullRequest: pullRequest); - expect( - verify( - mockGithubChecksUtil.updateCheckRun( - any, - any, - any, - status: captureAnyNamed('status'), - conclusion: captureAnyNamed('conclusion'), - output: anyNamed('output'), - ), - ).captured, - [CheckRunStatus.completed, CheckRunConclusion.success], - ); - }); - - test('ci.yaml validation fails with empty config', () async { - httpClient = MockClient((http.Request request) async { - if (request.url.path.contains('.ci.yaml')) { - return http.Response('', 200); - } - throw Exception('Failed to find ${request.url.path}'); - }); - await scheduler.triggerPresubmitTargets(pullRequest: pullRequest); - expect( - verify( - mockGithubChecksUtil.updateCheckRun( - any, - any, - any, - status: captureAnyNamed('status'), - conclusion: captureAnyNamed('conclusion'), - output: anyNamed('output'), - ), - ).captured, - [CheckRunStatus.completed, CheckRunConclusion.failure], - ); - }); - - test('ci.yaml validation fails on not enabled branch', () async { - final PullRequest pullRequest = generatePullRequest(branch: 'not-valid'); - await scheduler.triggerPresubmitTargets(pullRequest: pullRequest); - expect( - verify( - mockGithubChecksUtil.updateCheckRun( - any, - any, - any, - status: captureAnyNamed('status'), - conclusion: captureAnyNamed('conclusion'), - output: anyNamed('output'), - ), - ).captured, - [CheckRunStatus.completed, CheckRunConclusion.failure], - ); - }); - - test('ci.yaml validation fails with config with unknown dependencies', () async { - httpClient = MockClient((http.Request request) async { - if (request.url.path.contains('.ci.yaml')) { - return http.Response( - ''' -enabled_branches: - - master -targets: - - name: A - builder: Linux A - dependencies: - - B - ''', - 200, - ); - } - throw Exception('Failed to find ${request.url.path}'); - }); - await scheduler.triggerPresubmitTargets(pullRequest: pullRequest); - expect( - verify( - mockGithubChecksUtil.updateCheckRun( - any, - any, - any, - status: anyNamed('status'), - conclusion: anyNamed('conclusion'), - output: captureAnyNamed('output'), - ), - ).captured.first.text, - 'FormatException: ERROR: A depends on B which does not exist', - ); - }); - - test('retries only triggers failed builds only', () async { - final MockBuildBucketClient mockBuildbucket = MockBuildBucketClient(); - buildStatusService = - FakeBuildStatusService(commitStatuses: [CommitStatus(generateCommit(1), const [])]); - final FakePubSub pubsub = FakePubSub(); - scheduler = Scheduler( - cache: cache, - config: config, - datastoreProvider: (DatastoreDB db) => DatastoreService(db, 2), - githubChecksService: GithubChecksService(config, githubChecksUtil: mockGithubChecksUtil), - buildStatusProvider: (_) => buildStatusService, - httpClientProvider: () => httpClient, - luciBuildService: FakeLuciBuildService( - config: config, - githubChecksUtil: mockGithubChecksUtil, - buildbucket: mockBuildbucket, - gerritService: FakeGerritService(branchesValue: ['master']), - pubsub: pubsub, - ), - ); - when(mockBuildbucket.batch(any)).thenAnswer( - (_) async => BatchResponse( - responses: [ - Response( - searchBuilds: SearchBuildsResponse( - builds: [ - generateBuild(1000, name: 'Linux', bucket: 'try'), - generateBuild(2000, name: 'Linux Coverage', bucket: 'try'), - generateBuild(3000, name: 'Mac', bucket: 'try', status: Status.scheduled), - generateBuild(4000, name: 'Windows', bucket: 'try', status: Status.started), - generateBuild(5000, name: 'Linux A', bucket: 'try', status: Status.failure), - ], - ), - ), - ], - ), - ); - when(mockBuildbucket.scheduleBuild(any)) - .thenAnswer((_) async => generateBuild(5001, name: 'Linux A', bucket: 'try', status: Status.scheduled)); - // Only Linux A should be retried - final Map checkRuns = { - 'Linux': createCheckRun(name: 'Linux', id: 100), - 'Linux Coverage': createCheckRun(name: 'Linux Coverage', id: 200), - 'Mac': createCheckRun(name: 'Mac', id: 300, status: CheckRunStatus.queued), - 'Windows': createCheckRun(name: 'Windows', id: 400, status: CheckRunStatus.inProgress), - 'Linux A': createCheckRun(name: 'Linux A', id: 500), - }; - when(mockGithubChecksUtil.allCheckRuns(any, any)).thenAnswer((_) async { - return checkRuns; - }); - - final CheckSuiteEvent checkSuiteEvent = - CheckSuiteEvent.fromJson(jsonDecode(checkSuiteTemplate('rerequested')) as Map); - await scheduler.retryPresubmitTargets( - pullRequest: pullRequest, - checkSuiteEvent: checkSuiteEvent, - ); - - expect(pubsub.messages.length, 1); - final BatchRequest batchRequest = pubsub.messages.single as BatchRequest; - expect(batchRequest.requests!.length, 1); - // Schedule build should have been sent - expect(batchRequest.requests!.single.scheduleBuild, isNotNull); - final ScheduleBuildRequest scheduleBuildRequest = batchRequest.requests!.single.scheduleBuild!; - // Verify expected parameters to schedule build - expect(scheduleBuildRequest.builderId.builder, 'Linux A'); - expect(scheduleBuildRequest.properties!['custom'], 'abc'); - }); - - test('pass github_build_label to properties', () async { - final MockBuildBucketClient mockBuildbucket = MockBuildBucketClient(); - buildStatusService = - FakeBuildStatusService(commitStatuses: [CommitStatus(generateCommit(1), const [])]); - final FakePubSub pubsub = FakePubSub(); - scheduler = Scheduler( - cache: cache, - config: config, - datastoreProvider: (DatastoreDB db) => DatastoreService(db, 2), - githubChecksService: GithubChecksService(config, githubChecksUtil: mockGithubChecksUtil), - buildStatusProvider: (_) => buildStatusService, - httpClientProvider: () => httpClient, - luciBuildService: FakeLuciBuildService( - config: config, - githubChecksUtil: mockGithubChecksUtil, - buildbucket: mockBuildbucket, - gerritService: FakeGerritService(branchesValue: ['master']), - pubsub: pubsub, - ), - ); - when(mockBuildbucket.batch(any)).thenAnswer( - (_) async => BatchResponse( - responses: [ - Response( - searchBuilds: SearchBuildsResponse( - builds: [ - generateBuild(1000, name: 'Linux', bucket: 'try'), - generateBuild(2000, name: 'Linux Coverage', bucket: 'try'), - generateBuild(3000, name: 'Mac', bucket: 'try', status: Status.scheduled), - generateBuild(4000, name: 'Windows', bucket: 'try', status: Status.started), - generateBuild(5000, name: 'Linux A', bucket: 'try', status: Status.failure), - ], - ), - ), - ], - ), - ); - when(mockBuildbucket.scheduleBuild(any)) - .thenAnswer((_) async => generateBuild(5001, name: 'Linux A', bucket: 'try', status: Status.scheduled)); - // Only Linux A should be retried - final Map checkRuns = { - 'Linux': createCheckRun(name: 'Linux', id: 100), - 'Linux Coverage': createCheckRun(name: 'Linux Coverage', id: 200), - 'Mac': createCheckRun(name: 'Mac', id: 300, status: CheckRunStatus.queued), - 'Windows': createCheckRun(name: 'Windows', id: 400, status: CheckRunStatus.inProgress), - 'Linux A': createCheckRun(name: 'Linux A', id: 500), - }; - when(mockGithubChecksUtil.allCheckRuns(any, any)).thenAnswer((_) async { - return checkRuns; - }); - - final CheckSuiteEvent checkSuiteEvent = - CheckSuiteEvent.fromJson(jsonDecode(checkSuiteTemplate('rerequested')) as Map); - await scheduler.retryPresubmitTargets( - pullRequest: pullRequest, - checkSuiteEvent: checkSuiteEvent, - ); - - expect(pubsub.messages.length, 1); - final BatchRequest batchRequest = pubsub.messages.single as BatchRequest; - expect(batchRequest.requests!.length, 1); - // Schedule build should have been sent - expect(batchRequest.requests!.single.scheduleBuild, isNotNull); - final ScheduleBuildRequest scheduleBuildRequest = batchRequest.requests!.single.scheduleBuild!; - // Verify expected parameters to schedule build - expect(scheduleBuildRequest.builderId.builder, 'Linux A'); - expect(scheduleBuildRequest.properties!['custom'], 'abc'); - }); - - test('triggers only specificed targets', () async { - final List presubmitTargets = [generateTarget(1), generateTarget(2)]; - final List presubmitTriggerTargets = scheduler.getTriggerList(presubmitTargets, ['Linux 1']); - expect(presubmitTriggerTargets.length, 1); - }); - - test('triggers all presubmit targets when trigger list is null', () async { - final List presubmitTargets = [generateTarget(1), generateTarget(2)]; - final List presubmitTriggerTargets = scheduler.getTriggerList(presubmitTargets, null); - expect(presubmitTriggerTargets.length, 2); - }); - - test('triggers all presubmit targets when trigger list is empty', () async { - final List presubmitTargets = [generateTarget(1), generateTarget(2)]; - final List presubmitTriggerTargets = scheduler.getTriggerList(presubmitTargets, []); - expect(presubmitTriggerTargets.length, 2); - }); - - test('triggers only targets that are contained in the trigger list', () async { - final List presubmitTargets = [generateTarget(1), generateTarget(2)]; - final List presubmitTriggerTargets = - scheduler.getTriggerList(presubmitTargets, ['Linux 1', 'Linux 3']); - expect(presubmitTriggerTargets.length, 1); - expect(presubmitTargets[0].value.name, 'Linux 1'); - }); - }); - }); -} - -CheckRun createCheckRun({String? name, required int id, CheckRunStatus status = CheckRunStatus.completed}) { - final int externalId = id * 2; - final String checkRunJson = - '{"name": "$name", "id": $id, "external_id": "{$externalId}", "status": "$status", "started_at": "2020-05-10T02:49:31Z", "head_sha": "the_sha", "check_suite": {"id": 456}}'; - return CheckRun.fromJson(jsonDecode(checkRunJson) as Map); -} - -String toSha(Commit commit) => commit.sha!; - -int toTimestamp(Commit commit) => commit.timestamp!; diff --git a/app_dart/test/src/bigquery/fake_tabledata_resource.dart b/app_dart/test/src/bigquery/fake_tabledata_resource.dart deleted file mode 100644 index 9b7581f0d..000000000 --- a/app_dart/test/src/bigquery/fake_tabledata_resource.dart +++ /dev/null @@ -1,47 +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/bigquery/v2.dart'; - -/// A fake bigquery tabledataresourceApi implementation. -/// -/// This tabledataResourceApi considers only a simple case -/// where we focus on the number of rows inserted. This can be -/// easily extended for other test cases. -class FakeTabledataResource implements TabledataResource { - List? rows; - @override - Future insertAll( - TableDataInsertAllRequest request, - String projectId, - String datasetId, - String tableId, { - String? $fields, - }) async { - rows = request.rows; - return TableDataInsertAllResponse.fromJson({}); - } - - @override - Future list( - String projectId, - String datasetId, - String tableId, { - int? maxResults, - String? selectedFields, - String? startIndex, - String? pageToken, - String? $fields, - }) async { - final List> tableRowList = >[]; - for (TableDataInsertAllRequestRows tableDataInsertAllRequestRows in rows!) { - final dynamic value = tableDataInsertAllRequestRows.json; - final List> tableCellList = >[]; - tableCellList.add({'v': value}); - tableRowList.add({'f': tableCellList}); - } - - return TableDataList.fromJson({'totalRows': rows!.length.toString(), 'rows': tableRowList}); - } -} diff --git a/app_dart/test/src/datastore/fake_config.dart b/app_dart/test/src/datastore/fake_config.dart deleted file mode 100644 index 5ef2add84..000000000 --- a/app_dart/test/src/datastore/fake_config.dart +++ /dev/null @@ -1,305 +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:appengine/appengine.dart'; -import 'package:cocoon_service/src/model/appengine/branch.dart'; -import 'package:cocoon_service/src/model/appengine/key_helper.dart'; -import 'package:cocoon_service/src/model/appengine/service_account_info.dart'; -import 'package:cocoon_service/src/service/bigquery.dart'; -import 'package:cocoon_service/src/service/config.dart'; -import 'package:cocoon_service/src/service/github_service.dart'; -import 'package:github/github.dart' as gh; -import 'package:googleapis/bigquery/v2.dart'; -import 'package:graphql/client.dart'; - -import '../request_handling/fake_authentication.dart'; -import '../service/fake_github_service.dart'; -import 'fake_datastore.dart'; - -// ignore: must_be_immutable -class FakeConfig implements Config { - FakeConfig({ - this.githubClient, - this.deviceLabServiceAccountValue, - this.maxTaskRetriesValue, - this.maxLuciTaskRetriesValue, - this.keyHelperValue, - this.oauthClientIdValue, - this.githubOAuthTokenValue, - this.mergeConflictPullRequestMessageValue = 'default mergeConflictPullRequestMessageValue', - this.missingTestsPullRequestMessageValue = 'default missingTestsPullRequestMessageValue', - this.wrongBaseBranchPullRequestMessageValue, - this.wrongHeadBranchPullRequestMessageValue, - this.releaseBranchPullRequestMessageValue, - this.webhookKeyValue, - this.loggingServiceValue, - this.tabledataResource, - this.githubService, - this.bigqueryService, - this.githubGraphQLClient, - this.rollerAccountsValue, - this.flutterBuildValue, - this.flutterBuildDescriptionValue, - this.maxRecordsValue, - this.flutterGoldPendingValue, - this.flutterGoldSuccessValue, - this.flutterGoldChangesValue, - this.flutterGoldAlertConstantValue, - this.flutterGoldInitialAlertValue, - this.flutterGoldFollowUpAlertValue, - this.flutterGoldDraftChangeValue, - this.flutterGoldStalePRValue, - this.postsubmitSupportedReposValue, - this.supportedBranchesValue, - this.supportedReposValue, - this.batchSizeValue, - this.backfillerTargetLimitValue, - this.backfillerCommitLimitValue, - this.issueAndPRLimitValue, - this.githubRequestDelayValue, - FakeDatastoreDB? dbValue, - }) : dbValue = dbValue ?? FakeDatastoreDB(); - - gh.GitHub? githubClient; - GraphQLClient? githubGraphQLClient; - TabledataResource? tabledataResource; - BigqueryService? bigqueryService; - GithubService? githubService; - FakeDatastoreDB dbValue; - ServiceAccountInfo? deviceLabServiceAccountValue; - int? maxTaskRetriesValue; - int? maxLuciTaskRetriesValue; - int? batchSizeValue; - FakeKeyHelper? keyHelperValue; - String? oauthClientIdValue; - String? githubOAuthTokenValue; - String mergeConflictPullRequestMessageValue; - String missingTestsPullRequestMessageValue; - String? wrongBaseBranchPullRequestMessageValue; - String? wrongHeadBranchPullRequestMessageValue; - String? releaseBranchPullRequestMessageValue; - String? webhookKeyValue; - String? flutterBuildValue; - String? flutterBuildDescriptionValue; - Logging? loggingServiceValue; - String? waitingForTreeToGoGreenLabelNameValue; - Set? rollerAccountsValue; - int? maxRecordsValue; - int? backfillerTargetLimitValue; - int? backfillerCommitLimitValue; - int? issueAndPRLimitValue; - String? flutterGoldPendingValue; - String? flutterGoldSuccessValue; - String? flutterGoldChangesValue; - String? flutterGoldAlertConstantValue; - String? flutterGoldInitialAlertValue; - String? flutterGoldFollowUpAlertValue; - String? flutterGoldDraftChangeValue; - String? flutterGoldStalePRValue; - List? supportedBranchesValue; - String? overrideTreeStatusLabelValue; - Set? supportedReposValue; - Set? postsubmitSupportedReposValue; - Duration? githubRequestDelayValue; - - @override - Future createGitHubClient({gh.PullRequest? pullRequest, gh.RepositorySlug? slug}) async => githubClient!; - - @override - gh.GitHub createGitHubClientWithToken(String token) => githubClient!; - - @override - Future createGitHubGraphQLClient() async => githubGraphQLClient!; - - @override - Future createTabledataResourceApi() async => tabledataResource!; - - @override - Future createBigQueryService() async => bigqueryService!; - - @override - Future createGithubService(gh.RepositorySlug slug) async => githubService ?? FakeGithubService(); - - @override - GithubService createGithubServiceWithToken(String token) => githubService!; - - @override - FakeDatastoreDB get db => dbValue; - - @override - Duration get githubRequestDelay => githubRequestDelayValue ?? Duration.zero; - - @override - int get maxTaskRetries => maxTaskRetriesValue!; - - /// Size of the shards to send to buildBucket when scheduling builds. - @override - int get schedulingShardSize => 5; - - @override - int get backfillerTargetLimit => backfillerTargetLimitValue ?? 50; - - @override - int get backfillerCommitLimit => backfillerCommitLimitValue ?? 50; - - @override - int get issueAndPRLimit => issueAndPRLimitValue ?? 2; - - @override - int get batchSize => batchSizeValue ?? 5; - - @override - int get maxLuciTaskRetries => maxLuciTaskRetriesValue!; - - @override - int get maxRecords => maxRecordsValue!; - - @override - String get flutterGoldPending => flutterGoldPendingValue!; - - @override - String get flutterGoldSuccess => flutterGoldSuccessValue!; - - @override - String get flutterGoldChanges => flutterGoldChangesValue!; - - @override - String get flutterGoldDraftChange => flutterGoldDraftChangeValue!; - - @override - String get flutterGoldStalePR => flutterGoldStalePRValue!; - - @override - String flutterGoldInitialAlert(String url) => flutterGoldInitialAlertValue!; - - @override - String flutterGoldFollowUpAlert(String url) => flutterGoldFollowUpAlertValue!; - - @override - String flutterGoldAlertConstant(gh.RepositorySlug slug) => flutterGoldAlertConstantValue!; - - @override - String flutterGoldCommentID(gh.PullRequest pr) => 'PR ${pr.number}, at ${pr.head!.sha}'; - - @override - Future get frobWebhookKey async => 'frob-webhook-key'; - - @override - int get commitNumber => 30; - - @override - KeyHelper get keyHelper => keyHelperValue!; - - @override - Future get oauthClientId async => oauthClientIdValue!; - - @override - Future get githubOAuthToken async => githubOAuthTokenValue ?? 'token'; - - @override - String get mergeConflictPullRequestMessage => mergeConflictPullRequestMessageValue; - - @override - String get missingTestsPullRequestMessage => missingTestsPullRequestMessageValue; - - @override - String get wrongBaseBranchPullRequestMessage => wrongBaseBranchPullRequestMessageValue!; - - @override - String wrongHeadBranchPullRequestMessage(String branch) => wrongHeadBranchPullRequestMessageValue!; - - @override - String get releaseBranchPullRequestMessage => releaseBranchPullRequestMessageValue!; - - @override - Future get webhookKey async => webhookKeyValue!; - - @override - String get flutterBuild => flutterBuildValue!; - - @override - String get flutterBuildDescription => - flutterBuildDescriptionValue ?? - 'Tree is currently broken. Please do not merge this ' - 'PR unless it contains a fix for the tree.'; - - @override - Logging get loggingService => loggingServiceValue!; - - @override - String get waitingForTreeToGoGreenLabelName => waitingForTreeToGoGreenLabelNameValue!; - - @override - Set get rollerAccounts => rollerAccountsValue!; - - @override - Future generateGithubToken(gh.RepositorySlug slug) { - throw UnimplementedError(); - } - - @override - Future generateJsonWebToken() { - throw UnimplementedError(); - } - - @override - Future get githubAppId => throw UnimplementedError(); - - @override - Future> get githubAppInstallations => throw UnimplementedError(); - - @override - Future get githubPrivateKey => throw UnimplementedError(); - - @override - Future get githubPublicKey => throw UnimplementedError(); - - @override - Future createDefaultGitHubService() async => githubService!; - - @override - Future get overrideTreeStatusLabel async => overrideTreeStatusLabelValue!; - - @override - String get defaultRecipeBundleRef => 'refs/heads/main'; - - @override - Future> get releaseAccounts async => ['dart-flutter-releaser']; - - @override - Set get supportedRepos => - supportedReposValue ?? - { - Config.flutterSlug, - Config.engineSlug, - Config.cocoonSlug, - Config.packagesSlug, - }; - - @override - Set get postsubmitSupportedRepos => - postsubmitSupportedReposValue ?? {Config.packagesSlug}; - - @override - Future> getBranches(gh.RepositorySlug slug) async { - if (supportedBranchesValue == null) { - throw Exception('Test must set suportedBranchesValue to be able to use Config.getBranches'); - } - return supportedBranchesValue!.map( - (String branch) => Branch( - key: db.emptyKey.append( - Branch, - id: '${slug.fullName}/$branch', - ), - ), - ); - } - - @override - String get autosubmitBot => 'auto-submit[bot]'; - - static const String revertOfLabel = 'revert of'; -} diff --git a/app_dart/test/src/datastore/fake_datastore.dart b/app_dart/test/src/datastore/fake_datastore.dart deleted file mode 100644 index f749dd42b..000000000 --- a/app_dart/test/src/datastore/fake_datastore.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:async'; -import 'dart:math' as math; - -import 'package:gcloud/datastore.dart' show Datastore, OrderDirection, DatastoreError; -import 'package:gcloud/db.dart'; - -/// Signature for a callback function that will be notified whenever a -/// [FakeQuery] is run. -/// -/// The `results` argument contains the provisional results of the query (after -/// [FakeQuery.limit] and [FakeQuery.offset] have been applied). Callers can -/// affect the results of the query by returning a different set of results -/// from the callback. -/// -/// The callback must not return null. -typedef QueryCallback> = Iterable Function(Iterable results); - -/// Signature for a callback function that will be notified whenever `commit()` -/// is called, either via [FakeDatastoreDB.commit] or [FakeTransaction.commit]. -/// -/// The `inserts` and `deletes` arguments represent the prospective mutations. -/// Both arguments are immutable. -/// -/// This callback will be invoked before any mutations are applied, so by -/// throwing an exception, callbacks can simulate a failed commit. -typedef CommitCallback = void Function(List> inserts, List> deletes); - -/// A fake datastore database implementation. -/// -/// This datastore's contents are stored in a single [values] map. Callers can -/// set up the map to populate the datastore in a way that works for their -/// test. -class FakeDatastoreDB implements DatastoreDB { - FakeDatastoreDB({ - Map, Model>? values, - Map>>? onQuery, - this.onCommit, - this.commitException = false, - }) : values = values ?? , Model>{}, - onQuery = onQuery ?? >>{}; - - final Map, Model> values; - final Map>> onQuery; - CommitCallback? onCommit; - // Flag used in tests whether the transaction commit throws exception. - bool? commitException; - - /// Adds a [QueryCallback] to the set of callbacks that will be notified when - /// queries are run. - /// - /// The [callback] argument will replace any existing callback that has been - /// specified for type `T`, as only one callback may exist per type. - void addOnQuery>(QueryCallback callback) { - onQuery[T] = (Iterable> results) { - return callback(results.cast()).cast>(); - }; - } - - @override - Future commit({List>? inserts, List>? deletes}) async { - inserts ??= >[]; - deletes ??= >[]; - if (onCommit != null) { - onCommit!(List>.unmodifiable(inserts), List>.unmodifiable(deletes)); - } - deletes.forEach(values.remove); - for (Model model in inserts) { - values[model.key] = model; - } - } - - @override - Datastore get datastore => throw UnimplementedError(); - - @override - Partition get defaultPartition => Partition(null); - - @override - Key get emptyKey => defaultPartition.emptyKey; - - @override - Future> lookup>(List> keys) async { - final List found = []; - for (Key key in keys) { - for (Model model in values.values) { - if (model.key.id == key.id) { - found.add(model as T?); - } - } - - if (found.isEmpty) { - throw KeyNotFoundException(key); - } - } - - return found; - } - - @override - Future lookupValue>(Key key, {T Function()? orElse}) async { - final List values = await lookup(>[key]); - T? value = values.single; - if (value == null) { - if (orElse != null) { - value = orElse(); - } else { - throw KeyNotFoundException(key); - } - } - return value; - } - - @override - ModelDB get modelDB => throw UnimplementedError(); - - @override - Partition newPartition(String namespace) => Partition(namespace); - - @override - FakeQuery query>({Partition? partition, Key? ancestorKey}) { - List results = values.values.whereType().toList(); - if (ancestorKey != null) { - results = results.where((T entity) => entity.parentKey == ancestorKey).toList(); - } - return FakeQuery._(this, results); - } - - @override - Future withTransaction(TransactionHandler transactionHandler) { - final FakeTransaction transaction = FakeTransaction._(this); - return transactionHandler(transaction); - } - - @override - Future lookupOrNull>(Key key) { - throw UnimplementedError(); - } -} - -/// A query that will return all values of type `T` that exist in the -/// [FakeDatastoreDB.values] map. -/// -/// This fake query respects [order], [limit], and [offset]. However, [filter] -/// may require local additions here to respect new filters. -class FakeQuery> implements Query { - FakeQuery._(this.db, this.results); - - final FakeDatastoreDB db; - final List filters = []; - final List orders = []; - - List results; - int start = 0; - int count = 100; - - @override - void filter(String filterString, Object? comparisonObject) { - // In production, Datastore filters cannot have a space at the end. - assert(filterString.trim() == filterString); - filters.add(FakeFilterSpec._(filterString, comparisonObject)); - } - - @override - void limit(int limit) { - assert(limit >= 1); - count = limit; - } - - @override - void offset(int offset) { - assert(offset >= 0); - start = offset; - } - - @override - void order(String orderString) { - if (orderString.startsWith('-')) { - orders.add(FakeOrderSpec._(orderString.substring(1), OrderDirection.Decending)); - } else { - orders.add(FakeOrderSpec._(orderString, OrderDirection.Ascending)); - } - } - - @override - Stream run() { - Iterable resultsView = results; - - for (FakeFilterSpec filter in filters) { - final String filterString = filter.filterString; - final Object? value = filter.comparisonObject; - if (filterString.contains('branch =') || - filterString.contains('head =') || - filterString.contains('pr =') || - filterString.contains('repository =') || - filterString.contains('name =')) { - resultsView = resultsView.where((T result) => result.toString().contains(value.toString())); - } - } - resultsView = resultsView.skip(start).take(count); - - if (db.onQuery.containsKey(T)) { - resultsView = db.onQuery[T]!(resultsView).cast(); - } - return Stream.fromIterable(resultsView); - } -} - -class FakeFilterSpec { - const FakeFilterSpec._(this.filterString, this.comparisonObject); - - final String filterString; - final Object? comparisonObject; - - @override - String toString() => 'FakeFilterSpec($filterString, $comparisonObject)'; -} - -class FakeOrderSpec { - const FakeOrderSpec._(this.fieldName, this.direction); - - final String fieldName; - final OrderDirection direction; -} - -/// A fake datastore transaction. -/// -/// This class keeps track of [inserts] and [deletes] and updates the parent -/// [FakeDatastoreDB] when the transaction is committed. -class FakeTransaction implements Transaction { - FakeTransaction._(this.db); - - final Map, Model> inserts = , Model>{}; - final Set> deletes = >{}; - bool sealed = false; - - @override - final FakeDatastoreDB db; - - @override - Future commit() async { - if (db.commitException!) { - throw DatastoreError(); - } - if (sealed) { - throw StateError('Transaction sealed'); - } - if (db.onCommit != null) { - db.onCommit!(List>.unmodifiable(inserts.values), List>.unmodifiable(deletes)); - } - for (MapEntry, Model> entry in inserts.entries) { - db.values[entry.key] = entry.value; - } - deletes.forEach(db.values.remove); - sealed = true; - } - - @override - Future> lookup>(List> keys) async { - final List results = []; - for (Key key in keys) { - if (deletes.contains(key)) { - // results.add(null); - } else if (inserts.containsKey(key)) { - results.add(inserts[key] as T); - } else if (db.values.containsKey(key)) { - results.add(db.values[key] as T); - } else { - // results.add(null); - } - } - return results; - } - - @override - Future lookupValue>(Key key, {T Function()? orElse}) async { - final List values = await lookup(>[key]); - T? value = values.single; - if (value == null) { - if (orElse != null) { - value = orElse(); - } else { - throw KeyNotFoundException(key); - } - } - return value; - } - - @override - Query query>(Key ancestorKey, {Partition? partition}) { - final List queryResults = [ - ...inserts.values.whereType(), - ...db.values.values.whereType(), - ]; - deletes.whereType().forEach(queryResults.remove); - return FakeQuery._(db, queryResults); - } - - @override - void queueMutations({List>? inserts, List>? deletes}) { - if (sealed) { - throw StateError('Transaction sealed'); - } - if (inserts != null) { - final math.Random random = math.Random(); - for (Model insert in inserts) { - Key key = insert.key; - if (key.id == null) { - key = Key(key.parent!, key.type, random.nextInt(math.pow(2, 20).toInt())); - } - this.inserts[key] = insert; - } - } - if (deletes != null) { - this.deletes.addAll(deletes); - } - } - - @override - Future rollback() async { - if (sealed) { - throw StateError('Transaction sealed'); - } - inserts.clear(); - deletes.clear(); - sealed = true; - } - - @override - Future lookupOrNull>(Key key) { - throw UnimplementedError(); - } -} diff --git a/app_dart/test/src/request_handling/api_request_handler_tester.dart b/app_dart/test/src/request_handling/api_request_handler_tester.dart deleted file mode 100644 index 9c8d6574f..000000000 --- a/app_dart/test/src/request_handling/api_request_handler_tester.dart +++ /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. - -import 'dart:async'; - -import 'package:cocoon_service/src/request_handling/api_request_handler.dart'; -import 'package:cocoon_service/src/request_handling/body.dart'; -import 'package:cocoon_service/src/request_handling/request_handler.dart'; -import 'package:meta/meta.dart'; - -import 'fake_authentication.dart'; -import 'request_handler_tester.dart'; - -class ApiRequestHandlerTester extends RequestHandlerTester { - ApiRequestHandlerTester({ - super.request, - FakeAuthenticatedContext? context, - Map? requestData, - }) : context = context ?? FakeAuthenticatedContext(), - requestData = requestData ?? {}; - - FakeAuthenticatedContext context; - Map requestData; - - @override - @protected - Future run(Future Function() callback) { - return super.run(() { - return runZoned>( - () { - return callback(); - }, - zoneValues: , Object>{ - ApiKey.authContext: context, - ApiKey.requestData: requestData, - }, - ); - }); - } -} diff --git a/app_dart/test/src/request_handling/fake_authentication.dart b/app_dart/test/src/request_handling/fake_authentication.dart deleted file mode 100644 index 312d20bb5..000000000 --- a/app_dart/test/src/request_handling/fake_authentication.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 'dart:io'; - -import 'package:appengine/appengine.dart'; -import 'package:cocoon_service/src/foundation/typedefs.dart'; -import 'package:cocoon_service/src/model/appengine/key_helper.dart'; -import 'package:cocoon_service/src/model/google/token_info.dart'; -import 'package:cocoon_service/src/request_handling/authentication.dart'; -import 'package:cocoon_service/src/request_handling/exceptions.dart'; -import 'package:cocoon_service/src/service/config.dart'; - -// ignore: must_be_immutable -class FakeAuthenticationProvider implements AuthenticationProvider { - FakeAuthenticationProvider({ - FakeClientContext? clientContext, - this.authenticated = true, - }) : clientContext = clientContext ?? FakeClientContext(); - - bool authenticated; - FakeClientContext clientContext; - - @override - Future authenticate(HttpRequest request) async { - if (authenticated) { - return FakeAuthenticatedContext(clientContext: clientContext); - } else { - throw const Unauthenticated('Not authenticated'); - } - } - - @override - Future authenticateToken(TokenInfo token, {ClientContext? clientContext, Logging? log}) async { - if (authenticated) { - return FakeAuthenticatedContext(clientContext: clientContext as FakeClientContext?); - } else { - throw const Unauthenticated('Not authenticated'); - } - } - - @override - ClientContextProvider get clientContextProvider => throw UnimplementedError(); - - @override - Config get config => throw UnimplementedError(); - - @override - HttpClientProvider get httpClientProvider => throw UnimplementedError(); - - @override - Future tokenInfo(HttpRequest request, {Logging? log, String tokenType = 'id_token'}) async { - return TokenInfo( - email: 'abc@gmail.com', - issued: DateTime.now(), - ); - } -} - -// ignore: must_be_immutable -class FakeAuthenticatedContext implements AuthenticatedContext { - FakeAuthenticatedContext({ - FakeClientContext? clientContext, - }) : clientContext = clientContext ?? FakeClientContext(); - - @override - FakeClientContext clientContext; -} - -class FakeClientContext implements ClientContext { - FakeClientContext({ - this.isDevelopmentEnvironment = true, - this.isProductionEnvironment = false, - FakeAppEngineContext? applicationContext, - }) : applicationContext = applicationContext ?? FakeAppEngineContext(); - - @override - FakeAppEngineContext applicationContext; - - @override - bool isDevelopmentEnvironment; - - @override - bool isProductionEnvironment; - - @override - late Services services; - - @override - String? traceId; -} - -class FakeAppEngineContext implements AppEngineContext { - @override - String applicationID = 'flutter-dashboard'; - - @override - late String fullQualifiedApplicationId; - - @override - late String instance; - - @override - String? instanceId; - - @override - late bool isDevelopmentEnvironment; - - @override - late String module; - - @override - String partition = '[default]'; - - @override - late String version; -} - -class FakeKeyHelper extends KeyHelper { - FakeKeyHelper({ - AppEngineContext? applicationContext, - }) : super(applicationContext: applicationContext ?? FakeAppEngineContext()); -} diff --git a/app_dart/test/src/request_handling/fake_http.dart b/app_dart/test/src/request_handling/fake_http.dart deleted file mode 100644 index 7dc37856c..000000000 --- a/app_dart/test/src/request_handling/fake_http.dart +++ /dev/null @@ -1,668 +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 'dart:io'; -import 'dart:typed_data'; - -import 'package:meta/meta.dart'; - -typedef ContentLengthProvider = int Function(); - -@immutable -class _Body { - _Body.empty() - : isUtf8 = true, - value = null, - bytes = Uint8List(0), - stream = Stream.fromIterable(const Iterable.empty()); - - _Body.utf8(String this.value) - : isUtf8 = true, - bytes = utf8.encode(value), - stream = Stream.fromIterable([utf8.encode(value)]); - - _Body.rawBytes(this.bytes) - : isUtf8 = false, - value = null, - stream = Stream.fromIterable([bytes]); - - _Body.copy(_Body other) - : isUtf8 = other.isUtf8, - value = other.value, - bytes = other.bytes, - stream = Stream.fromIterable([other.bytes]); - - final bool isUtf8; - final String? value; - final Uint8List bytes; - final Stream stream; -} - -abstract class FakeTransport { - int get contentLength; - - HttpConnectionInfo? get connectionInfo => null; - - final List cookies = []; - - FakeHttpHeaders get headers { - _headers ??= FakeHttpHeaders(contentLengthProvider: () => contentLength); - return _headers!; - } - - FakeHttpHeaders? _headers; - - bool get persistentConnection => false; -} - -// TODO(tvolkert): `implements Stream` once HttpClientResponse does the same -abstract class FakeInbound extends FakeTransport { - FakeInbound(String? body) : _body = body == null ? _Body.empty() : _Body.utf8(body); - - /// Indicates whether the body stream has been exposed to callers in any way. - /// Once the body stream has been exposed to callers, [body] becomes - /// immutable. - bool _isStreamExposed = false; - - /// Resets this transport so that it may be reused. - @mustCallSuper - void reset() { - _body = _Body.copy(_body); - _isStreamExposed = false; - } - - /// The UTF-8 encoded value of the HTTP request body, or null if this request - /// specifies no body. - /// - /// If the HTTP request body was set via [bodyBytes], then it's assumed that - /// the body is not a UTF-8 encoded string, and subsequently attempting to - /// access [body] will throw a [StateError]. - /// - /// Once the body stream has been exposed to callers in any way, the [body] - /// value becomes immutable (as does the [bodyBytes] value), and any attempt - /// to modify it will throw a [StateError]. - String? get body { - if (!_body.isUtf8) { - throw StateError('body is not a valid UTF-8 string'); - } - return _body.value; - } - - _Body _body; - set body(String? value) { - if (_isStreamExposed) { - throw StateError('The body of this transport has been made immutable'); - } - _body = value == null ? _Body.empty() : _Body.utf8(value); - } - - /// The raw bytes of the HTTP request body. - /// - /// This will never be null; if the HTTP request body is empty, this will be - /// the empty list. - /// - /// Setting this value directly will be assumed to be because the bytes are - /// not a UTF-8 encoded string, and subsequently attempting to access [body] - /// will throw a [StateError]. - /// - /// Once the body stream has been exposed to callers in any way, the - /// [bodyBytes] value becomes immutable (as does the [body] value), and any - /// attempt to modify it will throw a [StateError]. - Uint8List get bodyBytes => _body.bytes; - set bodyBytes(Uint8List value) { - if (_isStreamExposed) { - throw StateError('The body of this transport has been made immutable'); - } - _body = _Body.rawBytes(value); - } - - StreamSubscription listen( - void Function(Uint8List event)? onData, { - Function? onError, - void Function()? onDone, - bool? cancelOnError, - }) { - _isStreamExposed = true; - return _body.stream.listen( - onData, - onError: onError, - onDone: onDone, - cancelOnError: cancelOnError, - ); - } - - Future any(bool Function(Uint8List element) test) { - _isStreamExposed = true; - return _body.stream.any(test); - } - - Stream asBroadcastStream({ - void Function(StreamSubscription subscription)? onListen, - void Function(StreamSubscription subscription)? onCancel, - }) { - _isStreamExposed = true; - return _body.stream.asBroadcastStream(onListen: onListen, onCancel: onCancel); - } - - Stream asyncExpand(Stream? Function(Uint8List event) convert) { - _isStreamExposed = true; - return _body.stream.asyncExpand(convert); - } - - Stream asyncMap(FutureOr Function(Uint8List event) convert) { - _isStreamExposed = true; - return _body.stream.asyncMap(convert); - } - - Stream cast() { - _isStreamExposed = true; - return _body.stream.cast(); - } - - Future contains(Object? needle) { - _isStreamExposed = true; - return _body.stream.contains(needle); - } - - Stream distinct([bool Function(Uint8List previous, Uint8List next)? equals]) { - _isStreamExposed = true; - return _body.stream.distinct(equals); - } - - Future drain([E? futureValue]) { - _isStreamExposed = true; - return _body.stream.drain(futureValue); - } - - Future elementAt(int index) { - _isStreamExposed = true; - return _body.stream.elementAt(index); - } - - Future every(bool Function(Uint8List element) test) { - _isStreamExposed = true; - return _body.stream.every(test); - } - - Stream expand(Iterable Function(Uint8List element) convert) { - _isStreamExposed = true; - return _body.stream.expand(convert); - } - - Future get first { - _isStreamExposed = true; - return _body.stream.first; - } - - Future firstWhere( - bool Function(Uint8List element) test, { - List Function()? orElse, - }) { - _isStreamExposed = true; - return _body.stream.firstWhere(test, orElse: () => Uint8List.fromList(orElse!())); - } - - Future fold(S initialValue, S Function(S previous, Uint8List element) combine) { - _isStreamExposed = true; - return _body.stream.fold(initialValue, combine); - } - - Future forEach(void Function(Uint8List element) action) { - _isStreamExposed = true; - return _body.stream.forEach(action); - } - - Stream handleError( - Function onError, { - bool Function(dynamic error)? test, - }) { - _isStreamExposed = true; - return _body.stream.handleError(onError, test: test); - } - - bool get isBroadcast { - _isStreamExposed = true; - return _body.stream.isBroadcast; - } - - Future get isEmpty { - _isStreamExposed = true; - return _body.stream.isEmpty; - } - - Future join([String separator = '']) { - _isStreamExposed = true; - return _body.stream.join(separator); - } - - Future get last { - _isStreamExposed = true; - return _body.stream.last; - } - - Future lastWhere( - bool Function(Uint8List element) test, { - Uint8List Function()? orElse, - }) { - _isStreamExposed = true; - return _body.stream.lastWhere(test, orElse: () => Uint8List.fromList(orElse!())); - } - - Future get length { - _isStreamExposed = true; - return _body.stream.length; - } - - Stream map(S Function(Uint8List event) convert) { - _isStreamExposed = true; - return _body.stream.map(convert); - } - - Future pipe(StreamConsumer streamConsumer) { - _isStreamExposed = true; - return _body.stream.map((Uint8List list) => list.toList()).pipe(streamConsumer); - } - - Future reduce(List Function(Uint8List previous, Uint8List element) combine) { - _isStreamExposed = true; - return _body.stream - .reduce((Uint8List previous, Uint8List element) => Uint8List.fromList(combine(previous, element))); - } - - Future get single { - _isStreamExposed = true; - return _body.stream.single; - } - - Future singleWhere( - bool Function(Uint8List element) test, { - List Function()? orElse, - }) { - _isStreamExposed = true; - return _body.stream.singleWhere(test, orElse: () => Uint8List.fromList(orElse!())); - } - - Stream skip(int count) { - _isStreamExposed = true; - return _body.stream.skip(count); - } - - Stream skipWhile(bool Function(Uint8List element) test) { - _isStreamExposed = true; - return _body.stream.skipWhile(test); - } - - Stream take(int count) { - _isStreamExposed = true; - return _body.stream.take(count); - } - - Stream takeWhile(bool Function(Uint8List element) test) { - _isStreamExposed = true; - return _body.stream.takeWhile(test); - } - - Stream timeout( - Duration timeLimit, { - void Function(EventSink sink)? onTimeout, - }) { - _isStreamExposed = true; - return _body.stream.timeout(timeLimit, onTimeout: onTimeout); - } - - Future> toList() { - _isStreamExposed = true; - return _body.stream.toList(); - } - - Future> toSet() { - _isStreamExposed = true; - return _body.stream.toSet(); - } - - Stream transform(StreamTransformer, S> streamTransformer) { - _isStreamExposed = true; - return _body.stream.map((Uint8List list) => list.toList()).transform(streamTransformer); - } - - Stream where(bool Function(Uint8List event) test) { - _isStreamExposed = true; - return _body.stream.where(test); - } - - @override - int get contentLength => _body.bytes.length; - - X509Certificate? get certificate => null; -} - -abstract class FakeOutbound extends FakeTransport implements IOSink { - StringBuffer _buffer = StringBuffer(); - - String get body => _buffer.toString(); - - List get errors => _errors; - List _errors = []; - - /// Whether this outbound has been closed. - bool get isClosed => _isClosed; - bool _isClosed = false; - - /// Resets this transport so that it may be reused. - @mustCallSuper - void reset() { - _isClosed = false; - _buffer = StringBuffer(); - _errors = []; - } - - @override - Encoding get encoding => utf8; - - @override - set encoding(Encoding value) => throw UnsupportedError('Unsupported'); - - @override - void add(List data) { - if (isClosed) { - throw StateError('Transport is closed'); - } - headers._sealed = true; - _buffer.write(utf8.decode(data)); - } - - @override - void addError(Object error, [StackTrace? stackTrace]) { - if (isClosed) { - throw StateError('Transport is closed'); - } - errors.add(error); - } - - @override - Future addStream(Stream> stream) async { - if (isClosed) { - throw StateError('Transport is closed'); - } - headers._sealed = true; - _buffer.write(await utf8.decoder.bind(stream).join()); - } - - @override - void write(Object? obj) { - if (isClosed) { - throw StateError('Transport is closed'); - } - headers._sealed = true; - _buffer.write(obj); - } - - @override - void writeAll(Iterable objects, [String separator = '']) { - if (isClosed) { - throw StateError('Transport is closed'); - } - headers._sealed = true; - _buffer.writeAll(objects, separator); - } - - @override - void writeCharCode(int charCode) { - if (isClosed) { - throw StateError('Transport is closed'); - } - headers._sealed = true; - _buffer.writeCharCode(charCode); - } - - @override - void writeln([Object? obj = '']) { - if (isClosed) { - throw StateError('Transport is closed'); - } - headers._sealed = true; - _buffer.writeln(obj); - } - - @override - Future get done async {} - - @override - Future flush() async {} - - @override - Future close() async { - _isClosed = true; - } - - bool get bufferOutput => false; - - set bufferOutput(bool value) => throw UnsupportedError('Unsupported'); - - @override - int get contentLength => _contentLength ?? _buffer.length; - int? _contentLength; - - set contentLength(int value) { - _contentLength = value; - } - - set persistentConnection(bool value) => throw UnsupportedError('Unsupported'); -} - -abstract class FakeCookie implements Cookie { - FakeCookie({ - required this.name, - required this.value, - this.domain, - this.path, - this.expires, - required this.httpOnly, - this.maxAge, - required this.secure, - }); - - @override - String name; - - @override - String value; - - @override - String? domain; - - @override - String? path; - - @override - DateTime? expires; - - @override - bool httpOnly; - - @override - int? maxAge; - - @override - bool secure; -} - -class FakeHttpHeaders implements HttpHeaders { - FakeHttpHeaders({this.contentLengthProvider}); - - final ContentLengthProvider? contentLengthProvider; - final Map> _values = >{}; - bool _sealed = false; - - void _checkSealed() { - if (_sealed) { - throw StateError('HTTP headers are sealed'); - } - } - - @override - bool get chunkedTransferEncoding => false; - - @override - set chunkedTransferEncoding(bool value) => throw UnsupportedError('Unsupported'); - - @override - int get contentLength => contentLengthProvider != null ? contentLengthProvider!() : -1; - - @override - set contentLength(int value) => throw UnsupportedError('Unsupported'); - - @override - ContentType get contentType => throw UnimplementedError(); - - @override - set contentType(ContentType? value) { - _checkSealed(); - removeAll(HttpHeaders.contentTypeHeader); - add(HttpHeaders.contentTypeHeader, '$value'); - } - - @override - DateTime? date; - - @override - DateTime? expires; - - @override - String? host; - - @override - DateTime? ifModifiedSince; - - @override - late bool persistentConnection; - - @override - int? port; - - @override - List? operator [](String name) => _values[name]; - - @override - void add(String name, Object value, {bool preserveHeaderCase = false}) { - _checkSealed(); - name = name.toLowerCase(); - _values[name] ??= []; - _values[name]!.add('$value'); - } - - @override - void clear() { - _checkSealed(); - _values.clear(); - } - - @override - void forEach(void Function(String name, List values) f) { - _values.forEach(f); - } - - @override - void noFolding(String name) {} - - @override - void remove(String name, Object value) { - _checkSealed(); - name = name.toLowerCase(); - if (_values.containsKey('$value')) { - _values[name]!.remove('$value'); - } - } - - @override - void removeAll(String name) { - _checkSealed(); - name = name.toLowerCase(); - _values.remove(name); - } - - @override - void set(String name, Object value, {bool preserveHeaderCase = false}) { - _checkSealed(); - name = name.toLowerCase(); - _values[name] = ['$value']; - } - - @override - String? value(String name) { - final List? value = _values[name.toLowerCase()]; - return value?.single; - } -} - -class FakeHttpRequest extends FakeInbound implements HttpRequest { - /// Creates a new [FakeHttpRequest]. - /// - /// If the optional [body] argument is specified, the request stream will - /// yield the specified body value when UTF-8 decoded. By default, the - /// request stream will be empty. The [body] property can be modified until - /// the stream has been exposed to callers, at which time it becomes - /// immutable. - FakeHttpRequest({ - this.method = 'GET', - String? body, - String path = '/', - Map? queryParametersValue, - FakeHttpResponse? response, - }) : uri = Uri(path: path, queryParameters: queryParametersValue), - response = response ?? FakeHttpResponse(), - super(body); - - @override - String method; - - @override - Uri uri; - - String get path => uri.path; - set path(String value) { - uri = uri.replace(path: value); - } - - @override - FakeHttpResponse response; - - @override - String get protocolVersion => '1.1'; - - @override - Uri get requestedUri => uri; - - @override - HttpSession get session => throw UnsupportedError('Unsupported'); -} - -class FakeHttpResponse extends FakeOutbound implements HttpResponse { - @override - Duration? get deadline => null; - - @override - set deadline(Duration? value) => throw UnsupportedError('Unsupported'); - - @override - String get reasonPhrase => 'reason'; - - @override - set reasonPhrase(String value) => throw UnsupportedError('Unsupported'); - - @override - int statusCode = HttpStatus.ok; - - @override - Future redirect(Uri location, {int status = HttpStatus.movedTemporarily}) { - statusCode = status; - headers.add(HttpHeaders.locationHeader, '$location'); - return close(); - } - - @override - Future detachSocket({bool writeHeaders = true}) => throw UnsupportedError('Unsupported'); -} diff --git a/app_dart/test/src/request_handling/fake_pubsub.dart b/app_dart/test/src/request_handling/fake_pubsub.dart deleted file mode 100644 index 1810a7419..000000000 --- a/app_dart/test/src/request_handling/fake_pubsub.dart +++ /dev/null @@ -1,22 +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/request_handling/pubsub.dart'; -import 'package:googleapis/pubsub/v1.dart'; - -class FakePubSub extends PubSub { - List messages = []; - bool exceptionFlag = false; - int exceptionRepetition = 1; - - @override - Future> publish(String topicName, dynamic json) async { - if (exceptionFlag && exceptionRepetition > 0) { - exceptionRepetition--; - throw DetailedApiRequestError(500, 'test api error'); - } - messages.add(json); - return []; - } -} diff --git a/app_dart/test/src/request_handling/fake_request_handler.dart b/app_dart/test/src/request_handling/fake_request_handler.dart deleted file mode 100644 index 4a4c19acf..000000000 --- a/app_dart/test/src/request_handling/fake_request_handler.dart +++ /dev/null @@ -1,29 +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/request_handling/body.dart'; -import 'package:cocoon_service/src/request_handling/request_handler.dart'; - -// ignore: must_be_immutable -class FakeRequestHandler extends RequestHandler { - FakeRequestHandler({ - required this.body, - required super.config, - }); - - final Body body; - - int callCount = 0; - - @override - Future get() async { - callCount++; - return body; - } - - @override - Future post() async { - return body; - } -} diff --git a/app_dart/test/src/request_handling/no_auth_request_handler_tester.dart b/app_dart/test/src/request_handling/no_auth_request_handler_tester.dart deleted file mode 100644 index 167ca666b..000000000 --- a/app_dart/test/src/request_handling/no_auth_request_handler_tester.dart +++ /dev/null @@ -1,36 +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/src/request_handling/body.dart'; -import 'package:cocoon_service/src/request_handling/no_auth_request_handler.dart'; -import 'package:cocoon_service/src/request_handling/request_handler.dart'; -import 'package:meta/meta.dart'; - -import 'request_handler_tester.dart'; - -class NoAuthRequestHandlerTester extends RequestHandlerTester { - NoAuthRequestHandlerTester({ - super.request, - Map? requestData, - }) : requestData = requestData ?? {}; - - Map requestData; - - @override - @protected - Future run(Future Function() callback) { - return super.run(() { - return runZoned>( - () { - return callback(); - }, - zoneValues: , Object>{ - NoAuthKey.requestData: requestData, - }, - ); - }); - } -} diff --git a/app_dart/test/src/request_handling/request_handler_tester.dart b/app_dart/test/src/request_handling/request_handler_tester.dart deleted file mode 100644 index 956b2a456..000000000 --- a/app_dart/test/src/request_handling/request_handler_tester.dart +++ /dev/null @@ -1,55 +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/src/request_handling/body.dart'; -import 'package:cocoon_service/src/request_handling/request_handler.dart'; -import 'package:http/testing.dart' as http; -import 'package:meta/meta.dart'; - -import 'fake_http.dart'; - -class RequestHandlerTester { - RequestHandlerTester({ - FakeHttpRequest? request, - this.httpClient, - }) { - this.request = request ?? FakeHttpRequest(); - } - - FakeHttpRequest? request; - http.MockClient? httpClient; - - /// This tester's [FakeHttpResponse], derived from [request]. - FakeHttpResponse get response => request!.response; - - /// Executes [RequestHandler.get] on the specified [handler]. - Future get(RequestHandler handler) { - return run(() { - return handler.get(); // ignore: invalid_use_of_protected_member - }); - } - - /// Executes [RequestHandler.post] on the specified [handler]. - Future post(RequestHandler handler) { - return run(() { - return handler.post(); // ignore: invalid_use_of_protected_member - }); - } - - @protected - Future run(Future Function() callback) { - return runZoned>( - () { - return callback(); - }, - zoneValues: , Object?>{ - RequestKey.request: request, - RequestKey.response: response, - RequestKey.httpClient: httpClient, - }, - ); - } -} diff --git a/app_dart/test/src/request_handling/subscription_tester.dart b/app_dart/test/src/request_handling/subscription_tester.dart deleted file mode 100644 index d6cf64f5e..000000000 --- a/app_dart/test/src/request_handling/subscription_tester.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:cocoon_service/src/model/luci/push_message.dart'; -import 'package:cocoon_service/src/request_handling/api_request_handler.dart'; -import 'package:cocoon_service/src/request_handling/body.dart'; -import 'package:cocoon_service/src/request_handling/request_handler.dart'; -import 'package:cocoon_service/src/request_handling/subscription_handler.dart'; -import 'package:meta/meta.dart'; - -import 'fake_authentication.dart'; -import 'request_handler_tester.dart'; - -class SubscriptionTester extends RequestHandlerTester { - SubscriptionTester({ - super.request, - FakeAuthenticatedContext? context, - this.message = const PushMessage(), - }) : context = context ?? FakeAuthenticatedContext(); - - FakeAuthenticatedContext context; - PushMessage message; - - @override - @protected - Future run(Future Function() callback) { - return super.run(() { - return runZoned>( - () { - return callback(); - }, - zoneValues: , Object>{ - ApiKey.authContext: context, - PubSubKey.message: message, - }, - ); - }); - } -} diff --git a/app_dart/test/src/service/fake_auth_client.dart b/app_dart/test/src/service/fake_auth_client.dart deleted file mode 100644 index 93b502ad5..000000000 --- a/app_dart/test/src/service/fake_auth_client.dart +++ /dev/null @@ -1,74 +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:typed_data'; - -import 'package:googleapis_auth/googleapis_auth.dart'; -import 'package:http/http.dart' as http; - -class FakeAuthClient extends AutoRefreshingAuthClient { - FakeAuthClient(this.baseClient); - - final http.Client baseClient; - - @override - void close() => baseClient.close(); - - @override - Stream get credentialUpdates => throw UnimplementedError(); - - @override - AccessCredentials get credentials => throw UnimplementedError(); - - @override - Future delete(Uri url, {Map? headers, Object? body, Encoding? encoding}) async => - baseClient.delete( - url, - headers: headers, - encoding: encoding, - ); - @override - Future get(Uri url, {Map? headers}) async => baseClient.get(url, headers: headers); - - @override - Future head(Uri url, {Map? headers}) async => baseClient.head(url, headers: headers); - - @override - Future patch(Uri url, {Map? headers, Object? body, Encoding? encoding}) async => - baseClient.patch( - url, - headers: headers, - body: body, - encoding: encoding, - ); - - @override - Future post(Uri url, {Map? headers, Object? body, Encoding? encoding}) async => - baseClient.post( - url, - headers: headers, - body: body, - encoding: encoding, - ); - - @override - Future put(Uri url, {Map? headers, Object? body, Encoding? encoding}) async => - baseClient.put( - url, - headers: headers, - body: body, - encoding: encoding, - ); - - @override - Future read(Uri url, {Map? headers}) async => baseClient.read(url, headers: headers); - - @override - Future readBytes(Uri url, {Map? headers}) async => - baseClient.readBytes(url, headers: headers); - - @override - Future send(http.BaseRequest request) async => baseClient.send(request); -} diff --git a/app_dart/test/src/service/fake_bigquery_service.dart b/app_dart/test/src/service/fake_bigquery_service.dart deleted file mode 100644 index 7013242d7..000000000 --- a/app_dart/test/src/service/fake_bigquery_service.dart +++ /dev/null @@ -1,19 +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:cocoon_service/src/service/bigquery.dart'; -import 'package:googleapis/bigquery/v2.dart'; - -import '../utilities/mocks.dart'; - -class FakeBigqueryService extends BigqueryService { - FakeBigqueryService(this.jobsResource) : super(MockAccessClientProvider()); - - JobsResource jobsResource; - - @override - Future defaultJobs() async { - return jobsResource; - } -} diff --git a/app_dart/test/src/service/fake_build_status_provider.dart b/app_dart/test/src/service/fake_build_status_provider.dart deleted file mode 100644 index 1bc7cabed..000000000 --- a/app_dart/test/src/service/fake_build_status_provider.dart +++ /dev/null @@ -1,51 +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/service/build_status_provider.dart'; -import 'package:cocoon_service/src/service/datastore.dart'; -import 'package:github/github.dart'; - -class FakeBuildStatusService implements BuildStatusService { - FakeBuildStatusService({ - this.cumulativeStatus, - this.commitStatuses, - }); - - BuildStatus? cumulativeStatus; - List? commitStatuses; - - @override - Future calculateCumulativeStatus(RepositorySlug slug) async { - if (cumulativeStatus == null) { - throw AssertionError(); - } - return cumulativeStatus; - } - - @override - Stream retrieveCommitStatus({ - int limit = 100, - int? timestamp, - String? branch, - required RepositorySlug slug, - }) { - if (commitStatuses == null) { - throw AssertionError(); - } - commitStatuses!.sort((CommitStatus a, CommitStatus b) => a.commit.timestamp!.compareTo(b.commit.timestamp!)); - - return Stream.fromIterable( - commitStatuses!.where( - (CommitStatus commitStatus) => - ((commitStatus.commit.timestamp == null || timestamp == null) - ? true - : commitStatus.commit.timestamp! < timestamp) && - commitStatus.commit.branch == branch, - ), - ); - } - - @override - DatastoreService get datastoreService => throw UnimplementedError(); -} diff --git a/app_dart/test/src/service/fake_buildbucket.dart b/app_dart/test/src/service/fake_buildbucket.dart deleted file mode 100644 index 8d57b9925..000000000 --- a/app_dart/test/src/service/fake_buildbucket.dart +++ /dev/null @@ -1,150 +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/model/luci/buildbucket.dart'; -import 'package:cocoon_service/src/service/buildbucket.dart'; -import 'package:http/http.dart' as http; -import 'package:http/testing.dart'; - -/// Fake [BuildBucketClient] for handling requests to BuildBucket. -/// -/// By default, returns good responses but can be updated to throw exceptions. -// ignore: must_be_immutable -class FakeBuildBucketClient extends BuildBucketClient { - FakeBuildBucketClient({ - this.scheduleBuildResponse, - this.searchBuildsResponse, - this.batchResponse, - this.cancelBuildResponse, - this.getBuildResponse, - this.listBuildersResponse, - }) : super(httpClient: MockClient((_) async => http.Response('', 200))); - - Future? scheduleBuildResponse; - Future? searchBuildsResponse; - Future Function()? batchResponse; - Future? cancelBuildResponse; - Future? getBuildResponse; - Future? listBuildersResponse; - - @override - Future scheduleBuild( - ScheduleBuildRequest? request, { - String buildBucketUri = 'https://localhost/builds', - }) async => - (scheduleBuildResponse != null) - ? await scheduleBuildResponse! - : Build( - id: '123', - builderId: request!.builderId, - tags: request.tags, - ); - - @override - Future searchBuilds( - SearchBuildsRequest? request, { - String buildBucketUri = 'https://localhost/builds', - }) async => - (searchBuildsResponse != null) - ? await searchBuildsResponse! - : const SearchBuildsResponse( - builds: [ - Build( - id: '123', - builderId: BuilderId( - builder: 'builder_abc', - bucket: 'try', - project: 'flutter', - ), - tags: >{ - 'buildset': ['pr/git/12345', 'sha/git/259bcf77bd04e64ef2181caccc43eda57780cd21'], - 'cipd_version': ['refs/heads/main'], - 'github_link': ['https://github/flutter/flutter/pull/1'], - }, - input: Input( - properties: {'bringup': 'true'}, - ), - ), - ], - ); - - @override - Future batch( - BatchRequest request, { - String buildBucketUri = 'https://localhost/builds', - }) async { - if (batchResponse != null) { - return batchResponse!(); - } - final List responses = []; - for (Request request in request.requests!) { - if (request.cancelBuild != null) { - responses.add(Response(cancelBuild: await cancelBuild(request.cancelBuild))); - } else if (request.getBuild != null) { - responses.add(Response(getBuild: await getBuild(request.getBuild))); - } else if (request.scheduleBuild != null) { - responses.add(Response(scheduleBuild: await scheduleBuild(request.scheduleBuild))); - } else if (request.searchBuilds != null) { - responses.add(Response(searchBuilds: await searchBuilds(request.searchBuilds))); - } - } - return BatchResponse(responses: responses); - } - - @override - Future cancelBuild( - CancelBuildRequest? request, { - String buildBucketUri = 'https://localhost/builds', - }) async => - (cancelBuildResponse != null) - ? await cancelBuildResponse! - : Build( - id: request!.id, - builderId: const BuilderId( - bucket: 'try', - project: 'flutter', - builder: 'builder_abc', - ), - summaryMarkdown: request.summaryMarkdown, - ); - - @override - Future getBuild( - GetBuildRequest? request, { - String buildBucketUri = 'https://localhost/builds', - }) async => - (getBuildResponse != null) - ? await getBuildResponse! - : Build( - id: request!.id!, - builderId: request.builderId!, - number: request.buildNumber, - ); - - @override - Future listBuilders( - ListBuildersRequest? request, { - String buildBucketUri = 'https://localhost/builders', - }) async => - (listBuildersResponse != null) - ? await listBuildersResponse! - : const ListBuildersResponse( - builders: [ - BuilderItem( - id: BuilderId( - bucket: 'prod', - project: 'flutter', - builder: 'Linux_android A', - ), - ), - BuilderItem( - id: BuilderId( - bucket: 'prod', - project: 'flutter', - builder: 'Linux_android B', - ), - ), - ], - ); -} diff --git a/app_dart/test/src/service/fake_gerrit_service.dart b/app_dart/test/src/service/fake_gerrit_service.dart deleted file mode 100644 index e1f684e0e..000000000 --- a/app_dart/test/src/service/fake_gerrit_service.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 'package:cocoon_service/src/model/gerrit/commit.dart'; -import 'package:cocoon_service/src/request_handling/exceptions.dart'; -import 'package:cocoon_service/src/service/gerrit_service.dart'; -import 'package:collection/collection.dart'; -import 'package:github/github.dart'; -import 'package:http/testing.dart'; - -import '../datastore/fake_config.dart'; -import '../utilities/entity_generators.dart'; - -/// Fake [GerritService] for use in tests. -class FakeGerritService extends GerritService { - FakeGerritService({ - this.branchesValue = _defaultBranches, - this.commitsValue, - }) : super( - config: FakeConfig(), - httpClient: - MockClient((_) => throw const InternalServerError('FakeGerritService tried to make an http request')), - ); - - List branchesValue; - static const List _defaultBranches = ['main']; - - List? commitsValue; - final List _defaultCommits = [ - generateGerritCommit(1), - generateGerritCommit(2), - generateGerritCommit(3), - ]; - - @override - Future> branches(String repo, String project, {String? filterRegex}) async => branchesValue; - - @override - Future> commits(RepositorySlug slug, String branch) async => commitsValue ?? _defaultCommits; - - @override - Future createBranch(RepositorySlug slug, String branchName, String revision) async => Future.value(null); - - @override - Future findMirroredCommit(RepositorySlug slug, String sha) async { - final commits = await this.commits(slug, ''); - return commits.firstWhereOrNull((commit) => commit.commit == sha); - } -} diff --git a/app_dart/test/src/service/fake_github_service.dart b/app_dart/test/src/service/fake_github_service.dart deleted file mode 100644 index a0b9d6fee..000000000 --- a/app_dart/test/src/service/fake_github_service.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 'package:cocoon_service/src/service/github_service.dart'; -import 'package:github/github.dart'; - -import '../utilities/mocks.dart'; - -/// A fake GithubService implementation. -class FakeGithubService implements GithubService { - FakeGithubService({GitHub? client}) : github = client ?? MockGitHub(); - late List Function(String, int) listCommitsBranch; - late List Function(String?) listPullRequestsBranch; - - @override - final GitHub github; - - @override - Future> listBranchedCommits( - RepositorySlug slug, - String branch, - int? lastCommitTimestampMills, - ) async { - return listCommitsBranch(branch, lastCommitTimestampMills ?? 0); - } - - @override - Future> listPullRequests(RepositorySlug slug, String? branch) async { - return listPullRequestsBranch(branch); - } - - @override - Future> addIssueLabels( - RepositorySlug slug, - int issueNumber, - List labels, - ) async { - return []; - } - - @override - Future assignReviewer(RepositorySlug slug, {int? pullRequestNumber, String? reviewer}) async {} - - @override - Future createIssue( - RepositorySlug slug, { - String? title, - String? body, - List? labels, - String? assignee, - }) async { - return Issue(); - } - - @override - Future assignIssue( - RepositorySlug slug, { - int? issueNumber, - String? assignee, - }) async { - return; - } - - @override - Future createPullRequest( - RepositorySlug slug, { - String? title, - String? body, - String? commitMessage, - GitReference? baseRef, - List? entries, - }) async { - return PullRequest(); - } - - @override - Future getFileContent(RepositorySlug slug, String path, {String? ref}) async { - if (path == 'bin/internal/release-candidate-branch.version') { - if (ref == 'beta') { - return 'flutter-3.2-candidate.5\n'; - } else if (ref == 'stable') { - return 'flutter-2.13-candidate.0\n'; - } - return Future.value(''); - } else { - return Future.value(''); - } - } - - @override - Future> listFiles(PullRequest pullRequest) async { - return ['abc/def']; - } - - @override - Future getReference(RepositorySlug slug, String ref) async { - return GitReference(); - } - - @override - Future> listIssues( - RepositorySlug slug, { - List? labels, - String state = 'open', - }) async { - return []; - } - - @override - Future? getIssue( - RepositorySlug slug, { - int? issueNumber, - }) { - return null; - } - - @override - Future createComment( - RepositorySlug slug, { - int? issueNumber, - String? body, - }) async { - return null; - } - - @override - Future> replaceLabelsForIssue( - RepositorySlug slug, { - int? issueNumber, - List? labels, - }) async { - return []; - } - - @override - Future getRateLimit() { - throw UnimplementedError(); - } - - @override - Future getPullRequest(RepositorySlug slug, int number) async { - return PullRequest(); - } - - @override - Future> searchIssuesAndPRs( - RepositorySlug slug, - String query, { - String? sort, - int pages = 2, - }) async { - return []; - } -} diff --git a/app_dart/test/src/service/fake_graphql_client.dart b/app_dart/test/src/service/fake_graphql_client.dart deleted file mode 100644 index 9e1f9b223..000000000 --- a/app_dart/test/src/service/fake_graphql_client.dart +++ /dev/null @@ -1,127 +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:gql/ast.dart'; -import 'package:graphql/client.dart'; -import 'package:test/test.dart'; - -class FakeGraphQLClient implements GraphQLClient { - late QueryResult Function(MutationOptions) mutateResultForOptions; - late QueryResult Function(QueryOptions) queryResultForOptions; - - @override - late QueryManager queryManager; - - @override - Link get link => throw UnimplementedError(); - - final List queries = []; - final List mutations = []; - - @override - Future> mutate(MutationOptions options) async { - mutations.add(options); - return mutateResultForOptions(options) as QueryResult; - } - - @override - Future> query(QueryOptions options) async { - queries.add(options); - return queryResultForOptions(options) as QueryResult; - } - - void verifyQueries(List expected) { - expect(queries.length, expected.length); - for (int i = 0; i < queries.length; i++) { - expect( - queries[i].properties, - equals(expected[i].properties), - ); - } - } - - void verifyMutations(List expected) { - expect(mutations.length, expected.length); - for (int i = 0; i < mutations.length; i++) { - expect( - mutations[i].properties, - equals(expected[i].properties), - ); - } - } - - @override - late DefaultPolicies defaultPolicies; - - @override - Map readFragment(FragmentRequest fragmentRequest, {bool? optimistic = true}) { - throw UnimplementedError(); - } - - @override - Map readQuery(Request request, {bool? optimistic = true}) { - throw UnimplementedError(); - } - - @override - Future> resetStore({bool refetchQueries = true}) { - throw UnimplementedError(); - } - - @override - void writeFragment(FragmentRequest fragmentRequest, {bool? broadcast = true, Map? data}) {} - - @override - void writeQuery(Request request, {Map? data, bool? broadcast = true}) {} - - @override - GraphQLCache get cache => throw UnimplementedError(); - - @override - GraphQLClient copyWith({ - Link? link, - GraphQLCache? cache, - DefaultPolicies? defaultPolicies, - bool? alwaysRebroadcast, - }) { - throw UnimplementedError(); - } - - @override - Future> fetchMore( - FetchMoreOptions fetchMoreOptions, { - required QueryOptions originalOptions, - required QueryResult previousResult, - }) { - throw UnimplementedError(); - } - - @override - Stream> subscribe(SubscriptionOptions options) { - throw UnimplementedError(); - } - - @override - ObservableQuery watchMutation(WatchQueryOptions options) { - throw UnimplementedError(); - } - - @override - ObservableQuery watchQuery(WatchQueryOptions options) { - throw UnimplementedError(); - } -} - -QueryResult createFakeQueryResult({ - Map? data, - OperationException? exception, -}) => - QueryResult( - data: data, - exception: exception, - options: QueryOptions( - document: const DocumentNode(), - ), - source: QueryResultSource.network, - ); diff --git a/app_dart/test/src/service/fake_luci_build_service.dart b/app_dart/test/src/service/fake_luci_build_service.dart deleted file mode 100644 index 665008595..000000000 --- a/app_dart/test/src/service/fake_luci_build_service.dart +++ /dev/null @@ -1,32 +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:cocoon_service/src/foundation/github_checks_util.dart'; -import 'package:cocoon_service/src/request_handling/pubsub.dart'; -import 'package:cocoon_service/src/service/buildbucket.dart'; -import 'package:cocoon_service/src/service/cache_service.dart'; -import 'package:cocoon_service/src/service/gerrit_service.dart'; -import 'package:cocoon_service/src/service/luci_build_service.dart'; - -import '../request_handling/fake_pubsub.dart'; -import '../utilities/mocks.dart'; -import 'fake_buildbucket.dart'; -import 'fake_gerrit_service.dart'; - -/// Fake [LuciBuildService] for use in tests. -class FakeLuciBuildService extends LuciBuildService { - FakeLuciBuildService({ - required super.config, - BuildBucketClient? buildbucket, - GithubChecksUtil? githubChecksUtil, - GerritService? gerritService, - PubSub? pubsub, - }) : super( - cache: CacheService(inMemory: true), - buildBucketClient: buildbucket ?? FakeBuildBucketClient(), - githubChecksUtil: githubChecksUtil ?? MockGithubChecksUtil(), - gerritService: gerritService ?? FakeGerritService(), - pubsub: pubsub ?? FakePubSub(), - ); -} diff --git a/app_dart/test/src/service/fake_scheduler.dart b/app_dart/test/src/service/fake_scheduler.dart deleted file mode 100644 index c07e772bc..000000000 --- a/app_dart/test/src/service/fake_scheduler.dart +++ /dev/null @@ -1,294 +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:cocoon_service/src/foundation/github_checks_util.dart'; -import 'package:cocoon_service/src/model/appengine/commit.dart'; -import 'package:cocoon_service/src/model/ci_yaml/ci_yaml.dart'; -import 'package:cocoon_service/src/model/proto/protos.dart' as pb; -import 'package:cocoon_service/src/service/buildbucket.dart'; -import 'package:cocoon_service/src/service/cache_service.dart'; -import 'package:cocoon_service/src/service/config.dart'; -import 'package:cocoon_service/src/service/github_checks_service.dart'; -import 'package:cocoon_service/src/service/luci_build_service.dart'; -import 'package:cocoon_service/src/service/scheduler.dart'; -import 'package:github/github.dart'; -import 'package:retry/retry.dart'; - -import '../utilities/entity_generators.dart'; -import 'fake_luci_build_service.dart'; - -/// Fake for [Scheduler] to use for tests that rely on it. -class FakeScheduler extends Scheduler { - FakeScheduler({ - this.ciYaml, - LuciBuildService? luciBuildService, - BuildBucketClient? buildbucket, - required super.config, - GithubChecksUtil? githubChecksUtil, - }) : super( - cache: CacheService(inMemory: true), - githubChecksService: GithubChecksService( - config, - githubChecksUtil: githubChecksUtil, - ), - luciBuildService: luciBuildService ?? - FakeLuciBuildService( - config: config, - buildbucket: buildbucket, - githubChecksUtil: githubChecksUtil, - ), - ); - - final CiYaml _defaultConfig = emptyConfig; - - /// [CiYaml] value to be injected on [getCiYaml]. - CiYaml? ciYaml; - - @override - Future getCiYaml( - Commit commit, { - CiYaml? totCiYaml, - RetryOptions? retryOptions, - bool validate = false, - }) async => - ciYaml ?? _defaultConfig; - - @override - Future generateTotCommit({required String branch, required RepositorySlug slug}) async { - return generateCommit(1); - } - - int cancelPreSubmitTargetsCallCnt = 0; - - int get cancelPreSubmitTargetsCallCount { - return cancelPreSubmitTargetsCallCnt; - } - - void resetCancelPreSubmitTargetsCallCount() { - cancelPreSubmitTargetsCallCnt = 0; - } - - @override - Future cancelPreSubmitTargets({ - required PullRequest pullRequest, - String reason = 'Newer commit available', - }) async { - await super.cancelPreSubmitTargets(pullRequest: pullRequest); - cancelPreSubmitTargetsCallCnt++; - } - - int addPullRequestCallCnt = 0; - - int get addPullRequestCallCount { - return addPullRequestCallCnt; - } - - void resetAddPullRequestCallCount() { - addPullRequestCallCnt = 0; - } - - @override - Future addPullRequest( - PullRequest pr, - ) async { - await super.addPullRequest(pr); - addPullRequestCallCnt++; - } -} - -final CiYaml emptyConfig = CiYaml( - slug: Config.flutterSlug, - branch: Config.defaultBranch(Config.flutterSlug), - config: pb.SchedulerConfig( - enabledBranches: [ - Config.defaultBranch(Config.flutterSlug), - ], - targets: [ - pb.Target( - name: 'Linux A', - scheduler: pb.SchedulerSystem.luci, - ), - ], - ), -); - -CiYaml exampleConfig = CiYaml( - slug: Config.flutterSlug, - branch: Config.defaultBranch(Config.flutterSlug), - config: pb.SchedulerConfig( - enabledBranches: [ - Config.defaultBranch(Config.flutterSlug), - ], - targets: [ - pb.Target( - name: 'Linux A', - scheduler: pb.SchedulerSystem.luci, - ), - pb.Target( - name: 'Mac A', - scheduler: pb.SchedulerSystem.luci, - ), - pb.Target( - name: 'Windows A', - scheduler: pb.SchedulerSystem.luci, - ), - pb.Target( - name: 'Google Internal Roll', - presubmit: false, - postsubmit: true, - scheduler: pb.SchedulerSystem.google_internal, - ), - ], - ), -); - -CiYaml exampleBackfillConfig = CiYaml( - slug: Config.flutterSlug, - branch: Config.defaultBranch(Config.flutterSlug), - config: pb.SchedulerConfig( - enabledBranches: [ - Config.defaultBranch(Config.flutterSlug), - ], - targets: [ - pb.Target( - name: 'Linux A', - scheduler: pb.SchedulerSystem.luci, - postsubmit: true, - properties: {'backfill': 'true'}, - ), - pb.Target( - name: 'Mac A', - scheduler: pb.SchedulerSystem.luci, - postsubmit: true, - ), - pb.Target( - name: 'Windows A', - scheduler: pb.SchedulerSystem.luci, - postsubmit: true, - properties: {'backfill': 'false'}, - ), - ], - ), -); - -CiYaml examplePresubmitRescheduleConfig = CiYaml( - slug: Config.flutterSlug, - branch: Config.defaultBranch(Config.flutterSlug), - config: pb.SchedulerConfig( - enabledBranches: [ - Config.defaultBranch(Config.flutterSlug), - ], - targets: [ - pb.Target( - name: 'Linux A', - ), - pb.Target( - name: 'Linux B', - postsubmit: true, - properties: {'presubmit_retry': '1'}, - ), - ], - ), -); - -final CiYaml batchPolicyConfig = CiYaml( - slug: Config.flutterSlug, - branch: Config.defaultBranch(Config.flutterSlug), - config: pb.SchedulerConfig( - enabledBranches: [ - Config.defaultBranch(Config.flutterSlug), - ], - targets: [ - pb.Target( - name: 'Linux_android A', - ), - pb.Target( - name: 'Linux_android B', - ), - pb.Target( - name: 'Linux_android C', - ), - ], - ), -); - -final CiYaml unsupportedPostsubmitCheckrunConfig = CiYaml( - slug: Config.flutterSlug, - branch: Config.defaultBranch(Config.flutterSlug), - config: pb.SchedulerConfig( - enabledBranches: [ - Config.defaultBranch(Config.flutterSlug), - ], - targets: [ - pb.Target( - name: 'Linux flutter', - ), - ], - ), -); - -final CiYaml nonBringupPackagesConfig = CiYaml( - slug: Config.packagesSlug, - branch: Config.defaultBranch(Config.packagesSlug), - config: pb.SchedulerConfig( - enabledBranches: [ - Config.defaultBranch(Config.packagesSlug), - ], - targets: [ - pb.Target( - name: 'Linux nonbringup', - ), - ], - ), -); - -final CiYaml bringupPackagesConfig = CiYaml( - slug: Config.packagesSlug, - branch: Config.defaultBranch(Config.packagesSlug), - config: pb.SchedulerConfig( - enabledBranches: [ - Config.defaultBranch(Config.packagesSlug), - ], - targets: [ - pb.Target( - name: 'Linux bringup', - bringup: true, - ), - ], - ), -); - -final CiYaml totCiYaml = CiYaml( - slug: Config.flutterSlug, - branch: Config.defaultBranch(Config.flutterSlug), - config: pb.SchedulerConfig( - enabledBranches: [ - Config.defaultBranch(Config.flutterSlug), - ], - targets: [ - pb.Target( - name: 'Linux_android B', - ), - pb.Target( - name: 'Linux_android C', - ), - ], - ), -); - -final CiYaml notInToTConfig = CiYaml( - slug: Config.flutterSlug, - branch: Config.defaultBranch(Config.flutterSlug), - config: pb.SchedulerConfig( - enabledBranches: [ - Config.defaultBranch(Config.flutterSlug), - ], - targets: [ - pb.Target( - name: 'Linux_android A', - ), - ], - ), - totConfig: totCiYaml, -); diff --git a/app_dart/test/src/utilities/entity_generators.dart b/app_dart/test/src/utilities/entity_generators.dart deleted file mode 100644 index a3ac9d972..000000000 --- a/app_dart/test/src/utilities/entity_generators.dart +++ /dev/null @@ -1,296 +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:cocoon_service/src/model/appengine/commit.dart'; -import 'package:cocoon_service/src/model/appengine/task.dart'; -import 'package:cocoon_service/src/model/ci_yaml/target.dart'; -import 'package:cocoon_service/src/model/gerrit/commit.dart'; -import 'package:cocoon_service/src/model/luci/buildbucket.dart'; -import 'package:cocoon_service/src/model/luci/push_message.dart' as push_message; -import 'package:cocoon_service/src/model/proto/protos.dart' as pb; -import 'package:gcloud/db.dart'; -import 'package:github/github.dart' as github; - -import '../service/fake_scheduler.dart'; - -Key generateKey(Type type, T id) => Key.emptyKey(Partition(null)).append(type, id: id); - -Commit generateCommit( - int i, { - String? sha, - String branch = 'master', - String? owner = 'flutter', - String repo = 'flutter', - int? timestamp, -}) => - Commit( - sha: sha ?? '$i', - timestamp: timestamp ?? i, - repository: '$owner/$repo', - branch: branch, - key: generateKey( - Commit, - '$owner/$repo/$branch/${sha ?? '$i'}', - ), - ); - -github.Branch generateBranch( - int i, { - String? name, - String? sha, -}) => - github.Branch( - name ?? '$i', - github.CommitData( - sha, - github.GitCommit(), - null, - null, - null, - null, - null, - null, - ), - ); - -github.Tag generateTag( - int i, { - String? name, - String? sha, -}) => - github.Tag( - name ?? '$i', - github.CommitInfo( - sha, - null, - ), - 'blah_zip', - 'blah_tar', - ); - -Task generateTask( - int i, { - String? name, - String status = Task.statusNew, - int attempts = 1, - bool isFlaky = false, - String stage = 'test-stage', - Commit? parent, - int? buildNumber, - DateTime? created, -}) => - Task( - name: name ?? 'task$i', - status: status, - commitKey: parent?.key ?? generateCommit(i).key, - key: (parent ?? generateCommit(i)).key.append(Task, id: i), - attempts: attempts, - isFlaky: isFlaky, - buildNumber: buildNumber, - buildNumberList: buildNumber != null ? '$buildNumber' : null, - createTimestamp: created?.millisecondsSinceEpoch ?? 0, - stageName: stage, - ); - -Target generateTarget( - int i, { - pb.SchedulerConfig? schedulerConfig, - String platform = 'Linux', - Map? platformProperties, - Map? platformDimensions, - Map? properties, - Map? dimensions, - List? runIf, - List? runIfNot, - bool? bringup, - github.RepositorySlug? slug, - pb.SchedulerSystem? schedulerSystem, -}) { - final pb.SchedulerConfig config = schedulerConfig ?? exampleConfig.config; - if (platformProperties != null && platformDimensions != null) { - config.platformProperties[platform.toLowerCase()] = - pb.SchedulerConfig_PlatformProperties(properties: platformProperties, dimensions: platformDimensions); - } else if (platformDimensions != null) { - config.platformProperties[platform.toLowerCase()] = - pb.SchedulerConfig_PlatformProperties(dimensions: platformDimensions); - } else if (platformProperties != null) { - config.platformProperties[platform.toLowerCase()] = - pb.SchedulerConfig_PlatformProperties(properties: platformProperties); - } - return Target( - schedulerConfig: config, - slug: slug ?? github.RepositorySlug('flutter', 'flutter'), - value: pb.Target( - name: '$platform $i', - properties: properties, - dimensions: dimensions, - runIf: runIf ?? [], - runIfNot: runIfNot ?? [], - bringup: bringup ?? false, - scheduler: schedulerSystem ?? pb.SchedulerSystem.cocoon, - ), - ); -} - -Build generateBuild( - int i, { - String bucket = 'prod', - String name = 'Linux test_builder', - Status status = Status.success, - Map>? tags, - Input? input, - int buildNumber = 1, -}) => - Build( - id: i.toString(), - builderId: BuilderId( - project: 'flutter', - bucket: bucket, - builder: name, - ), - status: status, - tags: tags, - number: buildNumber, - input: input, - ); - -push_message.Build generatePushMessageBuild( - int i, { - String bucket = 'prod', - String name = 'Linux test_builder', - push_message.Status? status = push_message.Status.completed, - push_message.Result result = push_message.Result.success, - List? tags, - int buildNumber = 1, - DateTime? completedTimestamp, - DateTime? createdTimestamp, - DateTime? startedTimestamp, - push_message.FailureReason? failureReason, -}) { - tags ??= []; - tags.add('build_address:luci.flutter.prod/$name/$buildNumber'); - - return push_message.Build( - bucket: bucket, - id: i.toString(), - project: 'flutter', - status: status, - result: result, - createdTimestamp: createdTimestamp, - completedTimestamp: completedTimestamp, - startedTimestamp: startedTimestamp, - tags: tags, - failureReason: failureReason, - ); -} - -github.CheckRun generateCheckRun( - int i, { - String name = 'name', - int checkSuite = 2, - DateTime? startedAt, -}) { - startedAt ??= DateTime.utc(2020, 05, 12); - return github.CheckRun.fromJson({ - 'id': i, - 'name': name, - 'started_at': startedAt.toIso8601String(), - 'check_suite': {'id': checkSuite}, - }); -} - -github.CheckSuite generateCheckSuite( - int i, { - String headBranch = 'main', - String headSha = 'abc', - github.CheckRunConclusion conclusion = github.CheckRunConclusion.success, - List pullRequests = const [], -}) { - return github.CheckSuite( - id: i, - headBranch: headBranch, - headSha: headSha, - conclusion: conclusion, - pullRequests: pullRequests, - ); -} - -github.PullRequest generatePullRequest({ - int id = 789, - String branch = 'master', - String repo = 'flutter', - String authorLogin = 'dash', - String authorAvatar = 'dashatar', - String title = 'example message', - int number = 123, - DateTime? mergedAt, - String sha = 'abc', - bool merged = true, - List labels = const [], -}) { - mergedAt ??= DateTime.fromMillisecondsSinceEpoch(1); - return github.PullRequest( - id: id, - title: title, - number: number, - mergedAt: mergedAt, - base: github.PullRequestHead( - ref: branch, - repo: github.Repository( - fullName: 'flutter/$repo', - name: repo, - owner: github.UserInformation('flutter', 1, '', ''), - ), - ), - head: github.PullRequestHead( - ref: branch, - sha: sha, - ), - user: github.User( - login: authorLogin, - avatarUrl: authorAvatar, - ), - mergeCommitSha: sha, - merged: merged, - labels: labels, - ); -} - -GerritCommit generateGerritCommit(int i) => GerritCommit( - commit: 'sha$i', - tree: 'main', - author: GerritUser( - email: 'dash@flutter.dev', - time: DateTime.fromMillisecondsSinceEpoch(i), - ), - ); - -github.RepositoryCommit generateGitCommit(int i) => github.RepositoryCommit( - commit: github.GitCommit( - committer: github.GitCommitUser( - 'dash', - 'dash@flutter.dev', - DateTime.fromMillisecondsSinceEpoch(i), - ), - ), - ); - -github.Issue generateIssue( - int i, { - String authorLogin = 'dash', - String authorAvatar = 'dashatar', - String title = 'example message', - int number = 123, -}) { - return github.Issue( - id: i, - title: title, - number: number, - user: github.User( - login: authorLogin, - avatarUrl: authorAvatar, - ), - ); -} diff --git a/app_dart/test/src/utilities/matchers.dart b/app_dart/test/src/utilities/matchers.dart deleted file mode 100644 index ce8605a15..000000000 --- a/app_dart/test/src/utilities/matchers.dart +++ /dev/null @@ -1,15 +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:test/test.dart'; - -Matcher throwsExceptionWith(String messageSubString) { - return throwsA( - isA().having( - (T e) => e.toString(), - 'description', - contains(messageSubString), - ), - ); -} diff --git a/app_dart/test/src/utilities/mocks.dart b/app_dart/test/src/utilities/mocks.dart deleted file mode 100644 index 8451cd1c8..000000000 --- a/app_dart/test/src/utilities/mocks.dart +++ /dev/null @@ -1,104 +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:io'; -import 'dart:typed_data'; - -import 'package:cocoon_service/src/foundation/github_checks_util.dart'; -import 'package:cocoon_service/src/request_handling/exceptions.dart'; -import 'package:cocoon_service/src/service/access_client_provider.dart'; -import 'package:cocoon_service/src/service/access_token_provider.dart'; -import 'package:cocoon_service/src/service/bigquery.dart'; -import 'package:cocoon_service/src/service/branch_service.dart'; -import 'package:cocoon_service/src/service/buildbucket.dart'; -import 'package:cocoon_service/src/service/commit_service.dart'; -import 'package:cocoon_service/src/service/config.dart'; -import 'package:cocoon_service/src/service/datastore.dart'; -import 'package:cocoon_service/src/service/github_checks_service.dart'; -import 'package:cocoon_service/src/service/github_service.dart'; -import 'package:cocoon_service/src/service/luci_build_service.dart'; -import 'package:github/github.dart'; -import 'package:googleapis/bigquery/v2.dart'; -import 'package:googleapis_auth/googleapis_auth.dart'; -import 'package:graphql/client.dart'; -import 'package:http/http.dart' as http; -import 'package:http/testing.dart'; -import 'package:mockito/annotations.dart'; -import 'package:neat_cache/neat_cache.dart'; -import 'package:process/process.dart'; - -import '../../service/cache_service_test.dart'; -import '../service/fake_auth_client.dart'; - -export 'mocks.mocks.dart'; - -/// Fallback generated function for returning default values from the generic -/// function [GitHub.postJSON]. -Future postJsonShim( - dynamic path, { - int? statusCode, - void Function(http.Response)? fail, - Map? headers, - Map? params, - T Function(S)? convert, - dynamic body, - String? preview, -}) { - if (T == PullRequest) { - return Future.value(PullRequest() as T); - } - throw Exception('MockGitHub.postJSON does not return $T.\n' - 'Either add it to postJsonShim or use a manual mock.'); -} - -Future authClientProviderShim({ - http.Client? baseClient, - required List scopes, -}) async => - FakeAuthClient(baseClient ?? MockClient((_) => throw const InternalServerError('Test did not set up HttpClient'))); - -@GenerateMocks( - [ - AccessClientProvider, - AccessTokenService, - BigqueryService, - BranchService, - BuildBucketClient, - CommitService, - Config, - DatastoreService, - FakeEntry, - IssuesService, - GithubChecksService, - GithubChecksUtil, - GithubService, - GitService, - GraphQLClient, - HttpClient, - HttpClientRequest, - HttpClientResponse, - JobsResource, - LuciBuildService, - ProcessManager, - PullRequestsService, - RepositoriesService, - SearchService, - TabledataResource, - UsersService, - ], - customMocks: [ - MockSpec>(), - MockSpec( - fallbackGenerators: { - #postJSON: postJsonShim, - }, - ), - ], -) -void main() {} - -class ThrowingGitHub implements GitHub { - @override - dynamic noSuchMethod(Invocation invocation) => throw AssertionError(); -} diff --git a/app_dart/test/src/utilities/mocks.mocks.dart b/app_dart/test/src/utilities/mocks.mocks.dart deleted file mode 100644 index a9f101f12..000000000 --- a/app_dart/test/src/utilities/mocks.mocks.dart +++ /dev/null @@ -1,8811 +0,0 @@ -// Mocks generated by Mockito 5.4.2 from annotations -// in cocoon_service/test/src/utilities/mocks.dart. -// Do not manually edit this file. - -// ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i19; -import 'dart:convert' as _i22; -import 'dart:io' as _i21; -import 'dart:typed_data' as _i35; - -import 'package:appengine/appengine.dart' as _i10; -import 'package:cocoon_service/cocoon_service.dart' as _i23; -import 'package:cocoon_service/src/foundation/github_checks_util.dart' as _i20; -import 'package:cocoon_service/src/model/appengine/branch.dart' as _i30; -import 'package:cocoon_service/src/model/appengine/commit.dart' as _i31; -import 'package:cocoon_service/src/model/appengine/github_build_status_update.dart' as _i17; -import 'package:cocoon_service/src/model/appengine/github_gold_status_update.dart' as _i18; -import 'package:cocoon_service/src/model/appengine/key_helper.dart' as _i12; -import 'package:cocoon_service/src/model/appengine/stage.dart' as _i33; -import 'package:cocoon_service/src/model/appengine/task.dart' as _i32; -import 'package:cocoon_service/src/model/ci_yaml/target.dart' as _i37; -import 'package:cocoon_service/src/model/github/checks.dart' as _i38; -import 'package:cocoon_service/src/model/luci/buildbucket.dart' as _i8; -import 'package:cocoon_service/src/model/luci/push_message.dart' as _i36; -import 'package:cocoon_service/src/service/access_client_provider.dart' as _i5; -import 'package:cocoon_service/src/service/access_token_provider.dart' as _i25; -import 'package:cocoon_service/src/service/bigquery.dart' as _i15; -import 'package:cocoon_service/src/service/commit_service.dart' as _i28; -import 'package:cocoon_service/src/service/config.dart' as _i3; -import 'package:cocoon_service/src/service/datastore.dart' as _i9; -import 'package:cocoon_service/src/service/gerrit_service.dart' as _i7; -import 'package:cocoon_service/src/service/github_service.dart' as _i16; -import 'package:gcloud/db.dart' as _i11; -import 'package:github/github.dart' as _i13; -import 'package:github/hooks.dart' as _i29; -import 'package:googleapis/bigquery/v2.dart' as _i6; -import 'package:googleapis_auth/auth_io.dart' as _i4; -import 'package:graphql/client.dart' as _i14; -import 'package:http/http.dart' as _i2; -import 'package:mockito/mockito.dart' as _i1; -import 'package:mockito/src/dummies.dart' as _i27; -import 'package:neat_cache/neat_cache.dart' as _i24; -import 'package:process/src/interface/process_manager.dart' as _i39; -import 'package:retry/retry.dart' as _i26; - -import '../../service/cache_service_test.dart' as _i34; -import 'mocks.dart' as _i40; - -// ignore_for_file: type=lint -// ignore_for_file: avoid_redundant_argument_values -// ignore_for_file: avoid_setters_without_getters -// ignore_for_file: comment_references -// ignore_for_file: implementation_imports -// ignore_for_file: invalid_use_of_visible_for_testing_member -// ignore_for_file: prefer_const_constructors -// ignore_for_file: unnecessary_parenthesis -// ignore_for_file: camel_case_types -// ignore_for_file: subtype_of_sealed_class - -class _FakeClient_0 extends _i1.SmartFake implements _i2.Client { - _FakeClient_0( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeConfig_1 extends _i1.SmartFake implements _i3.Config { - _FakeConfig_1( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeAccessToken_2 extends _i1.SmartFake implements _i4.AccessToken { - _FakeAccessToken_2( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeAccessClientProvider_3 extends _i1.SmartFake implements _i5.AccessClientProvider { - _FakeAccessClientProvider_3( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeTabledataResource_4 extends _i1.SmartFake implements _i6.TabledataResource { - _FakeTabledataResource_4( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeJobsResource_5 extends _i1.SmartFake implements _i6.JobsResource { - _FakeJobsResource_5( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeGerritService_6 extends _i1.SmartFake implements _i7.GerritService { - _FakeGerritService_6( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeBuild_7 extends _i1.SmartFake implements _i8.Build { - _FakeBuild_7( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeSearchBuildsResponse_8 extends _i1.SmartFake implements _i8.SearchBuildsResponse { - _FakeSearchBuildsResponse_8( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeBatchResponse_9 extends _i1.SmartFake implements _i8.BatchResponse { - _FakeBatchResponse_9( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeListBuildersResponse_10 extends _i1.SmartFake implements _i8.ListBuildersResponse { - _FakeListBuildersResponse_10( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeDatastoreService_11 extends _i1.SmartFake implements _i9.DatastoreService { - _FakeDatastoreService_11( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeLogging_12 extends _i1.SmartFake implements _i10.Logging { - _FakeLogging_12( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeDatastoreDB_13 extends _i1.SmartFake implements _i11.DatastoreDB { - _FakeDatastoreDB_13( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeKeyHelper_14 extends _i1.SmartFake implements _i12.KeyHelper { - _FakeKeyHelper_14( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeDuration_15 extends _i1.SmartFake implements Duration { - _FakeDuration_15( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeGitHub_16 extends _i1.SmartFake implements _i13.GitHub { - _FakeGitHub_16( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeGraphQLClient_17 extends _i1.SmartFake implements _i14.GraphQLClient { - _FakeGraphQLClient_17( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeBigqueryService_18 extends _i1.SmartFake implements _i15.BigqueryService { - _FakeBigqueryService_18( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeGithubService_19 extends _i1.SmartFake implements _i16.GithubService { - _FakeGithubService_19( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeGithubBuildStatusUpdate_20 extends _i1.SmartFake implements _i17.GithubBuildStatusUpdate { - _FakeGithubBuildStatusUpdate_20( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeGithubGoldStatusUpdate_21 extends _i1.SmartFake implements _i18.GithubGoldStatusUpdate { - _FakeGithubGoldStatusUpdate_21( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeFuture_22 extends _i1.SmartFake implements _i19.Future { - _FakeFuture_22( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeIssue_23 extends _i1.SmartFake implements _i13.Issue { - _FakeIssue_23( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeIssueComment_24 extends _i1.SmartFake implements _i13.IssueComment { - _FakeIssueComment_24( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeIssueLabel_25 extends _i1.SmartFake implements _i13.IssueLabel { - _FakeIssueLabel_25( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeMilestone_26 extends _i1.SmartFake implements _i13.Milestone { - _FakeMilestone_26( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeGithubChecksUtil_27 extends _i1.SmartFake implements _i20.GithubChecksUtil { - _FakeGithubChecksUtil_27( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeCheckRunConclusion_28 extends _i1.SmartFake implements _i13.CheckRunConclusion { - _FakeCheckRunConclusion_28( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeCheckRunStatus_29 extends _i1.SmartFake implements _i13.CheckRunStatus { - _FakeCheckRunStatus_29( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeCheckSuite_30 extends _i1.SmartFake implements _i13.CheckSuite { - _FakeCheckSuite_30( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeCheckRun_31 extends _i1.SmartFake implements _i13.CheckRun { - _FakeCheckRun_31( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakePullRequest_32 extends _i1.SmartFake implements _i13.PullRequest { - _FakePullRequest_32( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeGitReference_33 extends _i1.SmartFake implements _i13.GitReference { - _FakeGitReference_33( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeRateLimit_34 extends _i1.SmartFake implements _i13.RateLimit { - _FakeRateLimit_34( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeGitBlob_35 extends _i1.SmartFake implements _i13.GitBlob { - _FakeGitBlob_35( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeGitCommit_36 extends _i1.SmartFake implements _i13.GitCommit { - _FakeGitCommit_36( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeGitTag_37 extends _i1.SmartFake implements _i13.GitTag { - _FakeGitTag_37( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeGitTree_38 extends _i1.SmartFake implements _i13.GitTree { - _FakeGitTree_38( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeDefaultPolicies_39 extends _i1.SmartFake implements _i14.DefaultPolicies { - _FakeDefaultPolicies_39( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeLink_40 extends _i1.SmartFake implements _i14.Link { - _FakeLink_40( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeGraphQLCache_41 extends _i1.SmartFake implements _i14.GraphQLCache { - _FakeGraphQLCache_41( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeQueryManager_42 extends _i1.SmartFake implements _i14.QueryManager { - _FakeQueryManager_42( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeObservableQuery_43 extends _i1.SmartFake implements _i14.ObservableQuery { - _FakeObservableQuery_43( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeQueryResult_44 extends _i1.SmartFake implements _i14.QueryResult { - _FakeQueryResult_44( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeHttpClientRequest_45 extends _i1.SmartFake implements _i21.HttpClientRequest { - _FakeHttpClientRequest_45( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeUri_46 extends _i1.SmartFake implements Uri { - _FakeUri_46( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeHttpHeaders_47 extends _i1.SmartFake implements _i21.HttpHeaders { - _FakeHttpHeaders_47( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeHttpClientResponse_48 extends _i1.SmartFake implements _i21.HttpClientResponse { - _FakeHttpClientResponse_48( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeEncoding_49 extends _i1.SmartFake implements _i22.Encoding { - _FakeEncoding_49( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeSocket_50 extends _i1.SmartFake implements _i21.Socket { - _FakeSocket_50( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeStreamSubscription_51 extends _i1.SmartFake implements _i19.StreamSubscription { - _FakeStreamSubscription_51( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeJobCancelResponse_52 extends _i1.SmartFake implements _i6.JobCancelResponse { - _FakeJobCancelResponse_52( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeJob_53 extends _i1.SmartFake implements _i6.Job { - _FakeJob_53( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeGetQueryResultsResponse_54 extends _i1.SmartFake implements _i6.GetQueryResultsResponse { - _FakeGetQueryResultsResponse_54( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeJobList_55 extends _i1.SmartFake implements _i6.JobList { - _FakeJobList_55( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeQueryResponse_56 extends _i1.SmartFake implements _i6.QueryResponse { - _FakeQueryResponse_56( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeBuildBucketClient_57 extends _i1.SmartFake implements _i23.BuildBucketClient { - _FakeBuildBucketClient_57( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeCacheService_58 extends _i1.SmartFake implements _i23.CacheService { - _FakeCacheService_58( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakePubSub_59 extends _i1.SmartFake implements _i23.PubSub { - _FakePubSub_59( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeProcess_60 extends _i1.SmartFake implements _i21.Process { - _FakeProcess_60( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakePullRequestMerge_61 extends _i1.SmartFake implements _i13.PullRequestMerge { - _FakePullRequestMerge_61( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakePullRequestComment_62 extends _i1.SmartFake implements _i13.PullRequestComment { - _FakePullRequestComment_62( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakePullRequestReview_63 extends _i1.SmartFake implements _i13.PullRequestReview { - _FakePullRequestReview_63( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeRepository_64 extends _i1.SmartFake implements _i13.Repository { - _FakeRepository_64( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeLicenseDetails_65 extends _i1.SmartFake implements _i13.LicenseDetails { - _FakeLicenseDetails_65( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeLanguageBreakdown_66 extends _i1.SmartFake implements _i13.LanguageBreakdown { - _FakeLanguageBreakdown_66( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeBranch_67 extends _i1.SmartFake implements _i13.Branch { - _FakeBranch_67( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeCommitComment_68 extends _i1.SmartFake implements _i13.CommitComment { - _FakeCommitComment_68( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeRepositoryCommit_69 extends _i1.SmartFake implements _i13.RepositoryCommit { - _FakeRepositoryCommit_69( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeGitHubComparison_70 extends _i1.SmartFake implements _i13.GitHubComparison { - _FakeGitHubComparison_70( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeGitHubFile_71 extends _i1.SmartFake implements _i13.GitHubFile { - _FakeGitHubFile_71( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeRepositoryContents_72 extends _i1.SmartFake implements _i13.RepositoryContents { - _FakeRepositoryContents_72( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeContentCreation_73 extends _i1.SmartFake implements _i13.ContentCreation { - _FakeContentCreation_73( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeHook_74 extends _i1.SmartFake implements _i13.Hook { - _FakeHook_74( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakePublicKey_75 extends _i1.SmartFake implements _i13.PublicKey { - _FakePublicKey_75( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeRepositoryPages_76 extends _i1.SmartFake implements _i13.RepositoryPages { - _FakeRepositoryPages_76( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakePageBuild_77 extends _i1.SmartFake implements _i13.PageBuild { - _FakePageBuild_77( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeRelease_78 extends _i1.SmartFake implements _i13.Release { - _FakeRelease_78( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeReleaseAsset_79 extends _i1.SmartFake implements _i13.ReleaseAsset { - _FakeReleaseAsset_79( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeContributorParticipation_80 extends _i1.SmartFake implements _i13.ContributorParticipation { - _FakeContributorParticipation_80( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeRepositoryStatus_81 extends _i1.SmartFake implements _i13.RepositoryStatus { - _FakeRepositoryStatus_81( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeCombinedRepositoryStatus_82 extends _i1.SmartFake implements _i13.CombinedRepositoryStatus { - _FakeCombinedRepositoryStatus_82( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeReleaseNotes_83 extends _i1.SmartFake implements _i13.ReleaseNotes { - _FakeReleaseNotes_83( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeTableDataInsertAllResponse_84 extends _i1.SmartFake implements _i6.TableDataInsertAllResponse { - _FakeTableDataInsertAllResponse_84( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeTableDataList_85 extends _i1.SmartFake implements _i6.TableDataList { - _FakeTableDataList_85( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeUser_86 extends _i1.SmartFake implements _i13.User { - _FakeUser_86( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeCurrentUser_87 extends _i1.SmartFake implements _i13.CurrentUser { - _FakeCurrentUser_87( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeEntry_88 extends _i1.SmartFake implements _i24.Entry { - _FakeEntry_88( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeCache_89 extends _i1.SmartFake implements _i24.Cache { - _FakeCache_89( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeAuthentication_90 extends _i1.SmartFake implements _i13.Authentication { - _FakeAuthentication_90( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeActivityService_91 extends _i1.SmartFake implements _i13.ActivityService { - _FakeActivityService_91( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeAuthorizationsService_92 extends _i1.SmartFake implements _i13.AuthorizationsService { - _FakeAuthorizationsService_92( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeGistsService_93 extends _i1.SmartFake implements _i13.GistsService { - _FakeGistsService_93( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeGitService_94 extends _i1.SmartFake implements _i13.GitService { - _FakeGitService_94( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeIssuesService_95 extends _i1.SmartFake implements _i13.IssuesService { - _FakeIssuesService_95( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeMiscService_96 extends _i1.SmartFake implements _i13.MiscService { - _FakeMiscService_96( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeOrganizationsService_97 extends _i1.SmartFake implements _i13.OrganizationsService { - _FakeOrganizationsService_97( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakePullRequestsService_98 extends _i1.SmartFake implements _i13.PullRequestsService { - _FakePullRequestsService_98( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeRepositoriesService_99 extends _i1.SmartFake implements _i13.RepositoriesService { - _FakeRepositoriesService_99( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeSearchService_100 extends _i1.SmartFake implements _i13.SearchService { - _FakeSearchService_100( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeUrlShortenerService_101 extends _i1.SmartFake implements _i13.UrlShortenerService { - _FakeUrlShortenerService_101( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeUsersService_102 extends _i1.SmartFake implements _i13.UsersService { - _FakeUsersService_102( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeChecksService_103 extends _i1.SmartFake implements _i13.ChecksService { - _FakeChecksService_103( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeResponse_104 extends _i1.SmartFake implements _i2.Response { - _FakeResponse_104( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -/// A class which mocks [AccessClientProvider]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockAccessClientProvider extends _i1.Mock implements _i5.AccessClientProvider { - MockAccessClientProvider() { - _i1.throwOnMissingStub(this); - } - - @override - _i19.Future<_i2.Client> createAccessClient( - {List? scopes = const [r'https://www.googleapis.com/auth/cloud-platform']}) => - (super.noSuchMethod( - Invocation.method( - #createAccessClient, - [], - {#scopes: scopes}, - ), - returnValue: _i19.Future<_i2.Client>.value(_FakeClient_0( - this, - Invocation.method( - #createAccessClient, - [], - {#scopes: scopes}, - ), - )), - ) as _i19.Future<_i2.Client>); -} - -/// A class which mocks [AccessTokenService]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockAccessTokenService extends _i1.Mock implements _i25.AccessTokenService { - MockAccessTokenService() { - _i1.throwOnMissingStub(this); - } - - @override - _i3.Config get config => (super.noSuchMethod( - Invocation.getter(#config), - returnValue: _FakeConfig_1( - this, - Invocation.getter(#config), - ), - ) as _i3.Config); - @override - _i19.Future<_i4.AccessToken> createAccessToken() => (super.noSuchMethod( - Invocation.method( - #createAccessToken, - [], - ), - returnValue: _i19.Future<_i4.AccessToken>.value(_FakeAccessToken_2( - this, - Invocation.method( - #createAccessToken, - [], - ), - )), - ) as _i19.Future<_i4.AccessToken>); -} - -/// A class which mocks [BigqueryService]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockBigqueryService extends _i1.Mock implements _i15.BigqueryService { - MockBigqueryService() { - _i1.throwOnMissingStub(this); - } - - @override - _i5.AccessClientProvider get accessClientProvider => (super.noSuchMethod( - Invocation.getter(#accessClientProvider), - returnValue: _FakeAccessClientProvider_3( - this, - Invocation.getter(#accessClientProvider), - ), - ) as _i5.AccessClientProvider); - @override - _i19.Future<_i6.TabledataResource> defaultTabledata() => (super.noSuchMethod( - Invocation.method( - #defaultTabledata, - [], - ), - returnValue: _i19.Future<_i6.TabledataResource>.value(_FakeTabledataResource_4( - this, - Invocation.method( - #defaultTabledata, - [], - ), - )), - ) as _i19.Future<_i6.TabledataResource>); - @override - _i19.Future<_i6.JobsResource> defaultJobs() => (super.noSuchMethod( - Invocation.method( - #defaultJobs, - [], - ), - returnValue: _i19.Future<_i6.JobsResource>.value(_FakeJobsResource_5( - this, - Invocation.method( - #defaultJobs, - [], - ), - )), - ) as _i19.Future<_i6.JobsResource>); - @override - _i19.Future> listBuilderStatistic( - String? projectId, { - int? limit = 100, - String? bucket = r'prod', - }) => - (super.noSuchMethod( - Invocation.method( - #listBuilderStatistic, - [projectId], - { - #limit: limit, - #bucket: bucket, - }, - ), - returnValue: _i19.Future>.value(<_i15.BuilderStatistic>[]), - ) as _i19.Future>); - @override - _i19.Future> listRecentBuildRecordsForBuilder( - String? projectId, { - String? builder, - int? limit, - }) => - (super.noSuchMethod( - Invocation.method( - #listRecentBuildRecordsForBuilder, - [projectId], - { - #builder: builder, - #limit: limit, - }, - ), - returnValue: _i19.Future>.value(<_i15.BuilderRecord>[]), - ) as _i19.Future>); -} - -/// A class which mocks [BranchService]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockBranchService extends _i1.Mock implements _i23.BranchService { - MockBranchService() { - _i1.throwOnMissingStub(this); - } - - @override - _i3.Config get config => (super.noSuchMethod( - Invocation.getter(#config), - returnValue: _FakeConfig_1( - this, - Invocation.getter(#config), - ), - ) as _i3.Config); - @override - _i7.GerritService get gerritService => (super.noSuchMethod( - Invocation.getter(#gerritService), - returnValue: _FakeGerritService_6( - this, - Invocation.getter(#gerritService), - ), - ) as _i7.GerritService); - @override - _i26.RetryOptions get retryOptions => (super.noSuchMethod( - Invocation.getter(#retryOptions), - returnValue: _i27.dummyValue<_i26.RetryOptions>( - this, - Invocation.getter(#retryOptions), - ), - ) as _i26.RetryOptions); - @override - _i19.Future branchFlutterRecipes( - String? branch, - String? engineSha, - ) => - (super.noSuchMethod( - Invocation.method( - #branchFlutterRecipes, - [ - branch, - engineSha, - ], - ), - returnValue: _i19.Future.value(), - returnValueForMissingStub: _i19.Future.value(), - ) as _i19.Future); - @override - _i19.Future>> getReleaseBranches({ - required _i16.GithubService? githubService, - required _i13.RepositorySlug? slug, - }) => - (super.noSuchMethod( - Invocation.method( - #getReleaseBranches, - [], - { - #githubService: githubService, - #slug: slug, - }, - ), - returnValue: _i19.Future>>.value(>[]), - ) as _i19.Future>>); -} - -/// A class which mocks [BuildBucketClient]. -/// -/// See the documentation for Mockito's code generation for more information. -// ignore: must_be_immutable -class MockBuildBucketClient extends _i1.Mock implements _i23.BuildBucketClient { - MockBuildBucketClient() { - _i1.throwOnMissingStub(this); - } - - @override - String get buildBucketBuildUri => (super.noSuchMethod( - Invocation.getter(#buildBucketBuildUri), - returnValue: '', - ) as String); - @override - String get buildBucketBuilderUri => (super.noSuchMethod( - Invocation.getter(#buildBucketBuilderUri), - returnValue: '', - ) as String); - @override - _i2.Client get httpClient => (super.noSuchMethod( - Invocation.getter(#httpClient), - returnValue: _FakeClient_0( - this, - Invocation.getter(#httpClient), - ), - ) as _i2.Client); - @override - _i19.Future<_i8.Build> scheduleBuild( - _i8.ScheduleBuildRequest? request, { - String? buildBucketUri = r'https://cr-buildbucket.appspot.com/prpc/buildbucket.v2.Builds', - }) => - (super.noSuchMethod( - Invocation.method( - #scheduleBuild, - [request], - {#buildBucketUri: buildBucketUri}, - ), - returnValue: _i19.Future<_i8.Build>.value(_FakeBuild_7( - this, - Invocation.method( - #scheduleBuild, - [request], - {#buildBucketUri: buildBucketUri}, - ), - )), - ) as _i19.Future<_i8.Build>); - @override - _i19.Future<_i8.SearchBuildsResponse> searchBuilds( - _i8.SearchBuildsRequest? request, { - String? buildBucketUri = r'https://cr-buildbucket.appspot.com/prpc/buildbucket.v2.Builds', - }) => - (super.noSuchMethod( - Invocation.method( - #searchBuilds, - [request], - {#buildBucketUri: buildBucketUri}, - ), - returnValue: _i19.Future<_i8.SearchBuildsResponse>.value(_FakeSearchBuildsResponse_8( - this, - Invocation.method( - #searchBuilds, - [request], - {#buildBucketUri: buildBucketUri}, - ), - )), - ) as _i19.Future<_i8.SearchBuildsResponse>); - @override - _i19.Future<_i8.BatchResponse> batch( - _i8.BatchRequest? request, { - String? buildBucketUri = r'https://cr-buildbucket.appspot.com/prpc/buildbucket.v2.Builds', - }) => - (super.noSuchMethod( - Invocation.method( - #batch, - [request], - {#buildBucketUri: buildBucketUri}, - ), - returnValue: _i19.Future<_i8.BatchResponse>.value(_FakeBatchResponse_9( - this, - Invocation.method( - #batch, - [request], - {#buildBucketUri: buildBucketUri}, - ), - )), - ) as _i19.Future<_i8.BatchResponse>); - @override - _i19.Future<_i8.Build> cancelBuild( - _i8.CancelBuildRequest? request, { - String? buildBucketUri = r'https://cr-buildbucket.appspot.com/prpc/buildbucket.v2.Builds', - }) => - (super.noSuchMethod( - Invocation.method( - #cancelBuild, - [request], - {#buildBucketUri: buildBucketUri}, - ), - returnValue: _i19.Future<_i8.Build>.value(_FakeBuild_7( - this, - Invocation.method( - #cancelBuild, - [request], - {#buildBucketUri: buildBucketUri}, - ), - )), - ) as _i19.Future<_i8.Build>); - @override - _i19.Future<_i8.Build> getBuild( - _i8.GetBuildRequest? request, { - String? buildBucketUri = r'https://cr-buildbucket.appspot.com/prpc/buildbucket.v2.Builds', - }) => - (super.noSuchMethod( - Invocation.method( - #getBuild, - [request], - {#buildBucketUri: buildBucketUri}, - ), - returnValue: _i19.Future<_i8.Build>.value(_FakeBuild_7( - this, - Invocation.method( - #getBuild, - [request], - {#buildBucketUri: buildBucketUri}, - ), - )), - ) as _i19.Future<_i8.Build>); - @override - _i19.Future<_i8.ListBuildersResponse> listBuilders( - _i8.ListBuildersRequest? request, { - String? buildBucketUri = r'https://cr-buildbucket.appspot.com/prpc/buildbucket.v2.Builders', - }) => - (super.noSuchMethod( - Invocation.method( - #listBuilders, - [request], - {#buildBucketUri: buildBucketUri}, - ), - returnValue: _i19.Future<_i8.ListBuildersResponse>.value(_FakeListBuildersResponse_10( - this, - Invocation.method( - #listBuilders, - [request], - {#buildBucketUri: buildBucketUri}, - ), - )), - ) as _i19.Future<_i8.ListBuildersResponse>); - @override - void close() => super.noSuchMethod( - Invocation.method( - #close, - [], - ), - returnValueForMissingStub: null, - ); -} - -/// A class which mocks [CommitService]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockCommitService extends _i1.Mock implements _i28.CommitService { - MockCommitService() { - _i1.throwOnMissingStub(this); - } - - @override - _i3.Config get config => (super.noSuchMethod( - Invocation.getter(#config), - returnValue: _FakeConfig_1( - this, - Invocation.getter(#config), - ), - ) as _i3.Config); - @override - _i9.DatastoreServiceProvider get datastoreProvider => (super.noSuchMethod( - Invocation.getter(#datastoreProvider), - returnValue: (_i11.DatastoreDB db) => _FakeDatastoreService_11( - this, - Invocation.getter(#datastoreProvider), - ), - ) as _i9.DatastoreServiceProvider); - @override - _i19.Future handleCreateGithubRequest(_i29.CreateEvent? createEvent) => (super.noSuchMethod( - Invocation.method( - #handleCreateGithubRequest, - [createEvent], - ), - returnValue: _i19.Future.value(), - returnValueForMissingStub: _i19.Future.value(), - ) as _i19.Future); - @override - _i19.Future handlePushGithubRequest(Map? pushEvent) => (super.noSuchMethod( - Invocation.method( - #handlePushGithubRequest, - [pushEvent], - ), - returnValue: _i19.Future.value(), - returnValueForMissingStub: _i19.Future.value(), - ) as _i19.Future); -} - -/// A class which mocks [Config]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockConfig extends _i1.Mock implements _i3.Config { - MockConfig() { - _i1.throwOnMissingStub(this); - } - - @override - Set<_i13.RepositorySlug> get supportedRepos => (super.noSuchMethod( - Invocation.getter(#supportedRepos), - returnValue: <_i13.RepositorySlug>{}, - ) as Set<_i13.RepositorySlug>); - @override - Set<_i13.RepositorySlug> get postsubmitSupportedRepos => (super.noSuchMethod( - Invocation.getter(#postsubmitSupportedRepos), - returnValue: <_i13.RepositorySlug>{}, - ) as Set<_i13.RepositorySlug>); - @override - String get autosubmitBot => (super.noSuchMethod( - Invocation.getter(#autosubmitBot), - returnValue: '', - ) as String); - @override - _i10.Logging get loggingService => (super.noSuchMethod( - Invocation.getter(#loggingService), - returnValue: _FakeLogging_12( - this, - Invocation.getter(#loggingService), - ), - ) as _i10.Logging); - @override - _i19.Future get githubPrivateKey => (super.noSuchMethod( - Invocation.getter(#githubPrivateKey), - returnValue: _i19.Future.value(''), - ) as _i19.Future); - @override - _i19.Future get overrideTreeStatusLabel => (super.noSuchMethod( - Invocation.getter(#overrideTreeStatusLabel), - returnValue: _i19.Future.value(''), - ) as _i19.Future); - @override - _i19.Future get githubPublicKey => (super.noSuchMethod( - Invocation.getter(#githubPublicKey), - returnValue: _i19.Future.value(''), - ) as _i19.Future); - @override - _i19.Future get githubAppId => (super.noSuchMethod( - Invocation.getter(#githubAppId), - returnValue: _i19.Future.value(''), - ) as _i19.Future); - @override - _i19.Future> get githubAppInstallations => (super.noSuchMethod( - Invocation.getter(#githubAppInstallations), - returnValue: _i19.Future>.value({}), - ) as _i19.Future>); - @override - String get defaultRecipeBundleRef => (super.noSuchMethod( - Invocation.getter(#defaultRecipeBundleRef), - returnValue: '', - ) as String); - @override - _i11.DatastoreDB get db => (super.noSuchMethod( - Invocation.getter(#db), - returnValue: _FakeDatastoreDB_13( - this, - Invocation.getter(#db), - ), - ) as _i11.DatastoreDB); - @override - int get schedulingShardSize => (super.noSuchMethod( - Invocation.getter(#schedulingShardSize), - returnValue: 0, - ) as int); - @override - int get batchSize => (super.noSuchMethod( - Invocation.getter(#batchSize), - returnValue: 0, - ) as int); - @override - int get backfillerTargetLimit => (super.noSuchMethod( - Invocation.getter(#backfillerTargetLimit), - returnValue: 0, - ) as int); - @override - int get backfillerCommitLimit => (super.noSuchMethod( - Invocation.getter(#backfillerCommitLimit), - returnValue: 0, - ) as int); - @override - int get issueAndPRLimit => (super.noSuchMethod( - Invocation.getter(#issueAndPRLimit), - returnValue: 0, - ) as int); - @override - _i19.Future> get releaseAccounts => (super.noSuchMethod( - Invocation.getter(#releaseAccounts), - returnValue: _i19.Future>.value([]), - ) as _i19.Future>); - @override - _i19.Future get oauthClientId => (super.noSuchMethod( - Invocation.getter(#oauthClientId), - returnValue: _i19.Future.value(''), - ) as _i19.Future); - @override - _i19.Future get frobWebhookKey => (super.noSuchMethod( - Invocation.getter(#frobWebhookKey), - returnValue: _i19.Future.value(''), - ) as _i19.Future); - @override - _i19.Future get githubOAuthToken => (super.noSuchMethod( - Invocation.getter(#githubOAuthToken), - returnValue: _i19.Future.value(''), - ) as _i19.Future); - @override - String get wrongBaseBranchPullRequestMessage => (super.noSuchMethod( - Invocation.getter(#wrongBaseBranchPullRequestMessage), - returnValue: '', - ) as String); - @override - String get releaseBranchPullRequestMessage => (super.noSuchMethod( - Invocation.getter(#releaseBranchPullRequestMessage), - returnValue: '', - ) as String); - @override - _i19.Future get webhookKey => (super.noSuchMethod( - Invocation.getter(#webhookKey), - returnValue: _i19.Future.value(''), - ) as _i19.Future); - @override - String get mergeConflictPullRequestMessage => (super.noSuchMethod( - Invocation.getter(#mergeConflictPullRequestMessage), - returnValue: '', - ) as String); - @override - String get missingTestsPullRequestMessage => (super.noSuchMethod( - Invocation.getter(#missingTestsPullRequestMessage), - returnValue: '', - ) as String); - @override - String get flutterGoldPending => (super.noSuchMethod( - Invocation.getter(#flutterGoldPending), - returnValue: '', - ) as String); - @override - String get flutterGoldSuccess => (super.noSuchMethod( - Invocation.getter(#flutterGoldSuccess), - returnValue: '', - ) as String); - @override - String get flutterGoldChanges => (super.noSuchMethod( - Invocation.getter(#flutterGoldChanges), - returnValue: '', - ) as String); - @override - String get flutterGoldStalePR => (super.noSuchMethod( - Invocation.getter(#flutterGoldStalePR), - returnValue: '', - ) as String); - @override - String get flutterGoldDraftChange => (super.noSuchMethod( - Invocation.getter(#flutterGoldDraftChange), - returnValue: '', - ) as String); - @override - int get maxTaskRetries => (super.noSuchMethod( - Invocation.getter(#maxTaskRetries), - returnValue: 0, - ) as int); - @override - int get maxLuciTaskRetries => (super.noSuchMethod( - Invocation.getter(#maxLuciTaskRetries), - returnValue: 0, - ) as int); - @override - int get commitNumber => (super.noSuchMethod( - Invocation.getter(#commitNumber), - returnValue: 0, - ) as int); - @override - _i12.KeyHelper get keyHelper => (super.noSuchMethod( - Invocation.getter(#keyHelper), - returnValue: _FakeKeyHelper_14( - this, - Invocation.getter(#keyHelper), - ), - ) as _i12.KeyHelper); - @override - int get maxRecords => (super.noSuchMethod( - Invocation.getter(#maxRecords), - returnValue: 0, - ) as int); - @override - Duration get githubRequestDelay => (super.noSuchMethod( - Invocation.getter(#githubRequestDelay), - returnValue: _FakeDuration_15( - this, - Invocation.getter(#githubRequestDelay), - ), - ) as Duration); - @override - String get flutterBuild => (super.noSuchMethod( - Invocation.getter(#flutterBuild), - returnValue: '', - ) as String); - @override - String get flutterBuildDescription => (super.noSuchMethod( - Invocation.getter(#flutterBuildDescription), - returnValue: '', - ) as String); - @override - String get waitingForTreeToGoGreenLabelName => (super.noSuchMethod( - Invocation.getter(#waitingForTreeToGoGreenLabelName), - returnValue: '', - ) as String); - @override - Set get rollerAccounts => (super.noSuchMethod( - Invocation.getter(#rollerAccounts), - returnValue: {}, - ) as Set); - @override - _i19.Future> getBranches(_i13.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #getBranches, - [slug], - ), - returnValue: _i19.Future>.value(<_i30.Branch>[]), - ) as _i19.Future>); - @override - String wrongHeadBranchPullRequestMessage(String? branch) => (super.noSuchMethod( - Invocation.method( - #wrongHeadBranchPullRequestMessage, - [branch], - ), - returnValue: '', - ) as String); - @override - String flutterGoldInitialAlert(String? url) => (super.noSuchMethod( - Invocation.method( - #flutterGoldInitialAlert, - [url], - ), - returnValue: '', - ) as String); - @override - String flutterGoldFollowUpAlert(String? url) => (super.noSuchMethod( - Invocation.method( - #flutterGoldFollowUpAlert, - [url], - ), - returnValue: '', - ) as String); - @override - String flutterGoldAlertConstant(_i13.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #flutterGoldAlertConstant, - [slug], - ), - returnValue: '', - ) as String); - @override - String flutterGoldCommentID(_i13.PullRequest? pr) => (super.noSuchMethod( - Invocation.method( - #flutterGoldCommentID, - [pr], - ), - returnValue: '', - ) as String); - @override - _i19.Future generateJsonWebToken() => (super.noSuchMethod( - Invocation.method( - #generateJsonWebToken, - [], - ), - returnValue: _i19.Future.value(''), - ) as _i19.Future); - @override - _i19.Future generateGithubToken(_i13.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #generateGithubToken, - [slug], - ), - returnValue: _i19.Future.value(''), - ) as _i19.Future); - @override - _i19.Future<_i13.GitHub> createGitHubClient({ - _i13.PullRequest? pullRequest, - _i13.RepositorySlug? slug, - }) => - (super.noSuchMethod( - Invocation.method( - #createGitHubClient, - [], - { - #pullRequest: pullRequest, - #slug: slug, - }, - ), - returnValue: _i19.Future<_i13.GitHub>.value(_FakeGitHub_16( - this, - Invocation.method( - #createGitHubClient, - [], - { - #pullRequest: pullRequest, - #slug: slug, - }, - ), - )), - ) as _i19.Future<_i13.GitHub>); - @override - _i13.GitHub createGitHubClientWithToken(String? token) => (super.noSuchMethod( - Invocation.method( - #createGitHubClientWithToken, - [token], - ), - returnValue: _FakeGitHub_16( - this, - Invocation.method( - #createGitHubClientWithToken, - [token], - ), - ), - ) as _i13.GitHub); - @override - _i19.Future<_i14.GraphQLClient> createGitHubGraphQLClient() => (super.noSuchMethod( - Invocation.method( - #createGitHubGraphQLClient, - [], - ), - returnValue: _i19.Future<_i14.GraphQLClient>.value(_FakeGraphQLClient_17( - this, - Invocation.method( - #createGitHubGraphQLClient, - [], - ), - )), - ) as _i19.Future<_i14.GraphQLClient>); - @override - _i19.Future<_i15.BigqueryService> createBigQueryService() => (super.noSuchMethod( - Invocation.method( - #createBigQueryService, - [], - ), - returnValue: _i19.Future<_i15.BigqueryService>.value(_FakeBigqueryService_18( - this, - Invocation.method( - #createBigQueryService, - [], - ), - )), - ) as _i19.Future<_i15.BigqueryService>); - @override - _i19.Future<_i6.TabledataResource> createTabledataResourceApi() => (super.noSuchMethod( - Invocation.method( - #createTabledataResourceApi, - [], - ), - returnValue: _i19.Future<_i6.TabledataResource>.value(_FakeTabledataResource_4( - this, - Invocation.method( - #createTabledataResourceApi, - [], - ), - )), - ) as _i19.Future<_i6.TabledataResource>); - @override - _i19.Future<_i16.GithubService> createDefaultGitHubService() => (super.noSuchMethod( - Invocation.method( - #createDefaultGitHubService, - [], - ), - returnValue: _i19.Future<_i16.GithubService>.value(_FakeGithubService_19( - this, - Invocation.method( - #createDefaultGitHubService, - [], - ), - )), - ) as _i19.Future<_i16.GithubService>); - @override - _i19.Future<_i16.GithubService> createGithubService(_i13.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #createGithubService, - [slug], - ), - returnValue: _i19.Future<_i16.GithubService>.value(_FakeGithubService_19( - this, - Invocation.method( - #createGithubService, - [slug], - ), - )), - ) as _i19.Future<_i16.GithubService>); - @override - _i16.GithubService createGithubServiceWithToken(String? token) => (super.noSuchMethod( - Invocation.method( - #createGithubServiceWithToken, - [token], - ), - returnValue: _FakeGithubService_19( - this, - Invocation.method( - #createGithubServiceWithToken, - [token], - ), - ), - ) as _i16.GithubService); -} - -/// A class which mocks [DatastoreService]. -/// -/// See the documentation for Mockito's code generation for more information. -// ignore: must_be_immutable -class MockDatastoreService extends _i1.Mock implements _i9.DatastoreService { - MockDatastoreService() { - _i1.throwOnMissingStub(this); - } - - @override - int get maxEntityGroups => (super.noSuchMethod( - Invocation.getter(#maxEntityGroups), - returnValue: 0, - ) as int); - @override - _i11.DatastoreDB get db => (super.noSuchMethod( - Invocation.getter(#db), - returnValue: _FakeDatastoreDB_13( - this, - Invocation.getter(#db), - ), - ) as _i11.DatastoreDB); - @override - _i26.RetryOptions get retryOptions => (super.noSuchMethod( - Invocation.getter(#retryOptions), - returnValue: _i27.dummyValue<_i26.RetryOptions>( - this, - Invocation.getter(#retryOptions), - ), - ) as _i26.RetryOptions); - @override - _i19.Stream<_i31.Commit> queryRecentCommits({ - int? limit = 100, - int? timestamp, - String? branch, - required _i13.RepositorySlug? slug, - }) => - (super.noSuchMethod( - Invocation.method( - #queryRecentCommits, - [], - { - #limit: limit, - #timestamp: timestamp, - #branch: branch, - #slug: slug, - }, - ), - returnValue: _i19.Stream<_i31.Commit>.empty(), - ) as _i19.Stream<_i31.Commit>); - @override - _i19.Stream<_i30.Branch> queryBranches() => (super.noSuchMethod( - Invocation.method( - #queryBranches, - [], - ), - returnValue: _i19.Stream<_i30.Branch>.empty(), - ) as _i19.Stream<_i30.Branch>); - @override - _i19.Stream<_i32.Task> queryRecentTasksByName({ - int? limit = 100, - required String? name, - }) => - (super.noSuchMethod( - Invocation.method( - #queryRecentTasksByName, - [], - { - #limit: limit, - #name: name, - }, - ), - returnValue: _i19.Stream<_i32.Task>.empty(), - ) as _i19.Stream<_i32.Task>); - @override - _i19.Stream<_i32.FullTask> queryRecentTasks({ - String? taskName, - int? commitLimit = 20, - String? branch, - required _i13.RepositorySlug? slug, - }) => - (super.noSuchMethod( - Invocation.method( - #queryRecentTasks, - [], - { - #taskName: taskName, - #commitLimit: commitLimit, - #branch: branch, - #slug: slug, - }, - ), - returnValue: _i19.Stream<_i32.FullTask>.empty(), - ) as _i19.Stream<_i32.FullTask>); - @override - _i19.Future> queryTasksGroupedByStage(_i31.Commit? commit) => (super.noSuchMethod( - Invocation.method( - #queryTasksGroupedByStage, - [commit], - ), - returnValue: _i19.Future>.value(<_i33.Stage>[]), - ) as _i19.Future>); - @override - _i19.Future<_i17.GithubBuildStatusUpdate> queryLastStatusUpdate( - _i13.RepositorySlug? slug, - _i13.PullRequest? pr, - ) => - (super.noSuchMethod( - Invocation.method( - #queryLastStatusUpdate, - [ - slug, - pr, - ], - ), - returnValue: _i19.Future<_i17.GithubBuildStatusUpdate>.value(_FakeGithubBuildStatusUpdate_20( - this, - Invocation.method( - #queryLastStatusUpdate, - [ - slug, - pr, - ], - ), - )), - ) as _i19.Future<_i17.GithubBuildStatusUpdate>); - @override - _i19.Future<_i18.GithubGoldStatusUpdate> queryLastGoldUpdate( - _i13.RepositorySlug? slug, - _i13.PullRequest? pr, - ) => - (super.noSuchMethod( - Invocation.method( - #queryLastGoldUpdate, - [ - slug, - pr, - ], - ), - returnValue: _i19.Future<_i18.GithubGoldStatusUpdate>.value(_FakeGithubGoldStatusUpdate_21( - this, - Invocation.method( - #queryLastGoldUpdate, - [ - slug, - pr, - ], - ), - )), - ) as _i19.Future<_i18.GithubGoldStatusUpdate>); - @override - _i19.Future>>> shard(List<_i11.Model>? rows) => (super.noSuchMethod( - Invocation.method( - #shard, - [rows], - ), - returnValue: _i19.Future>>>.value(>>[]), - ) as _i19.Future>>>); - @override - _i19.Future insert(List<_i11.Model>? rows) => (super.noSuchMethod( - Invocation.method( - #insert, - [rows], - ), - returnValue: _i19.Future.value(), - returnValueForMissingStub: _i19.Future.value(), - ) as _i19.Future); - @override - _i19.Future> lookupByKey>(List<_i11.Key>? keys) => - (super.noSuchMethod( - Invocation.method( - #lookupByKey, - [keys], - ), - returnValue: _i19.Future>.value([]), - ) as _i19.Future>); - @override - _i19.Future lookupByValue>( - _i11.Key? key, { - T Function()? orElse, - }) => - (super.noSuchMethod( - Invocation.method( - #lookupByValue, - [key], - {#orElse: orElse}, - ), - returnValue: _i27.ifNotNull( - _i27.dummyValueOrNull( - this, - Invocation.method( - #lookupByValue, - [key], - {#orElse: orElse}, - ), - ), - (T v) => _i19.Future.value(v), - ) ?? - _FakeFuture_22( - this, - Invocation.method( - #lookupByValue, - [key], - {#orElse: orElse}, - ), - ), - ) as _i19.Future); - @override - _i19.Future withTransaction(_i19.Future Function(_i11.Transaction)? handler) => (super.noSuchMethod( - Invocation.method( - #withTransaction, - [handler], - ), - returnValue: _i19.Future.value(), - ) as _i19.Future); - @override - _i19.Future<_i32.Task?> getTaskFromBuildbucketBuild( - _i8.Build? build, { - String? customName, - }) => - (super.noSuchMethod( - Invocation.method( - #getTaskFromBuildbucketBuild, - [build], - {#customName: customName}, - ), - returnValue: _i19.Future<_i32.Task?>.value(), - ) as _i19.Future<_i32.Task?>); -} - -/// A class which mocks [FakeEntry]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockFakeEntry extends _i1.Mock implements _i34.FakeEntry { - MockFakeEntry() { - _i1.throwOnMissingStub(this); - } - - @override - _i35.Uint8List get value => (super.noSuchMethod( - Invocation.getter(#value), - returnValue: _i35.Uint8List(0), - ) as _i35.Uint8List); - @override - set value(_i35.Uint8List? _value) => super.noSuchMethod( - Invocation.setter( - #value, - _value, - ), - returnValueForMissingStub: null, - ); - @override - _i19.Future<_i35.Uint8List> get([ - _i19.Future<_i35.Uint8List?> Function()? create, - Duration? ttl, - ]) => - (super.noSuchMethod( - Invocation.method( - #get, - [ - create, - ttl, - ], - ), - returnValue: _i19.Future<_i35.Uint8List>.value(_i35.Uint8List(0)), - ) as _i19.Future<_i35.Uint8List>); - @override - _i19.Future purge({int? retries = 0}) => (super.noSuchMethod( - Invocation.method( - #purge, - [], - {#retries: retries}, - ), - returnValue: _i19.Future.value(), - returnValueForMissingStub: _i19.Future.value(), - ) as _i19.Future); - @override - _i19.Future<_i35.Uint8List?> set( - _i35.Uint8List? value, [ - Duration? ttl, - ]) => - (super.noSuchMethod( - Invocation.method( - #set, - [ - value, - ttl, - ], - ), - returnValue: _i19.Future<_i35.Uint8List?>.value(), - ) as _i19.Future<_i35.Uint8List?>); -} - -/// A class which mocks [IssuesService]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockIssuesService extends _i1.Mock implements _i13.IssuesService { - MockIssuesService() { - _i1.throwOnMissingStub(this); - } - - @override - _i13.GitHub get github => (super.noSuchMethod( - Invocation.getter(#github), - returnValue: _FakeGitHub_16( - this, - Invocation.getter(#github), - ), - ) as _i13.GitHub); - @override - _i19.Stream<_i13.Issue> listAll({ - int? milestoneNumber, - String? state, - String? direction, - String? sort, - DateTime? since, - int? perPage, - List? labels, - }) => - (super.noSuchMethod( - Invocation.method( - #listAll, - [], - { - #milestoneNumber: milestoneNumber, - #state: state, - #direction: direction, - #sort: sort, - #since: since, - #perPage: perPage, - #labels: labels, - }, - ), - returnValue: _i19.Stream<_i13.Issue>.empty(), - ) as _i19.Stream<_i13.Issue>); - @override - _i19.Stream<_i13.Issue> listByUser({ - int? milestoneNumber, - String? state, - String? direction, - String? sort, - DateTime? since, - int? perPage, - List? labels, - }) => - (super.noSuchMethod( - Invocation.method( - #listByUser, - [], - { - #milestoneNumber: milestoneNumber, - #state: state, - #direction: direction, - #sort: sort, - #since: since, - #perPage: perPage, - #labels: labels, - }, - ), - returnValue: _i19.Stream<_i13.Issue>.empty(), - ) as _i19.Stream<_i13.Issue>); - @override - _i19.Stream<_i13.Issue> listByOrg( - String? org, { - int? milestoneNumber, - String? state, - String? direction, - String? sort, - DateTime? since, - int? perPage, - List? labels, - }) => - (super.noSuchMethod( - Invocation.method( - #listByOrg, - [org], - { - #milestoneNumber: milestoneNumber, - #state: state, - #direction: direction, - #sort: sort, - #since: since, - #perPage: perPage, - #labels: labels, - }, - ), - returnValue: _i19.Stream<_i13.Issue>.empty(), - ) as _i19.Stream<_i13.Issue>); - @override - _i19.Stream<_i13.Issue> listByRepo( - _i13.RepositorySlug? slug, { - int? milestoneNumber, - String? state, - String? direction, - String? sort, - DateTime? since, - int? perPage, - List? labels, - }) => - (super.noSuchMethod( - Invocation.method( - #listByRepo, - [slug], - { - #milestoneNumber: milestoneNumber, - #state: state, - #direction: direction, - #sort: sort, - #since: since, - #perPage: perPage, - #labels: labels, - }, - ), - returnValue: _i19.Stream<_i13.Issue>.empty(), - ) as _i19.Stream<_i13.Issue>); - @override - _i19.Stream<_i13.Reaction> listReactions( - _i13.RepositorySlug? slug, - int? issueNumber, { - _i13.ReactionType? content, - }) => - (super.noSuchMethod( - Invocation.method( - #listReactions, - [ - slug, - issueNumber, - ], - {#content: content}, - ), - returnValue: _i19.Stream<_i13.Reaction>.empty(), - ) as _i19.Stream<_i13.Reaction>); - @override - _i19.Future<_i13.Issue> edit( - _i13.RepositorySlug? slug, - int? issueNumber, - _i13.IssueRequest? issue, - ) => - (super.noSuchMethod( - Invocation.method( - #edit, - [ - slug, - issueNumber, - issue, - ], - ), - returnValue: _i19.Future<_i13.Issue>.value(_FakeIssue_23( - this, - Invocation.method( - #edit, - [ - slug, - issueNumber, - issue, - ], - ), - )), - ) as _i19.Future<_i13.Issue>); - @override - _i19.Future<_i13.Issue> get( - _i13.RepositorySlug? slug, - int? issueNumber, - ) => - (super.noSuchMethod( - Invocation.method( - #get, - [ - slug, - issueNumber, - ], - ), - returnValue: _i19.Future<_i13.Issue>.value(_FakeIssue_23( - this, - Invocation.method( - #get, - [ - slug, - issueNumber, - ], - ), - )), - ) as _i19.Future<_i13.Issue>); - @override - _i19.Future<_i13.Issue> create( - _i13.RepositorySlug? slug, - _i13.IssueRequest? issue, - ) => - (super.noSuchMethod( - Invocation.method( - #create, - [ - slug, - issue, - ], - ), - returnValue: _i19.Future<_i13.Issue>.value(_FakeIssue_23( - this, - Invocation.method( - #create, - [ - slug, - issue, - ], - ), - )), - ) as _i19.Future<_i13.Issue>); - @override - _i19.Stream<_i13.User> listAssignees(_i13.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listAssignees, - [slug], - ), - returnValue: _i19.Stream<_i13.User>.empty(), - ) as _i19.Stream<_i13.User>); - @override - _i19.Future isAssignee( - _i13.RepositorySlug? slug, - String? repoName, - ) => - (super.noSuchMethod( - Invocation.method( - #isAssignee, - [ - slug, - repoName, - ], - ), - returnValue: _i19.Future.value(false), - ) as _i19.Future); - @override - _i19.Stream<_i13.IssueComment> listCommentsByIssue( - _i13.RepositorySlug? slug, - int? issueNumber, - ) => - (super.noSuchMethod( - Invocation.method( - #listCommentsByIssue, - [ - slug, - issueNumber, - ], - ), - returnValue: _i19.Stream<_i13.IssueComment>.empty(), - ) as _i19.Stream<_i13.IssueComment>); - @override - _i19.Stream<_i13.IssueComment> listCommentsByRepo(_i13.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listCommentsByRepo, - [slug], - ), - returnValue: _i19.Stream<_i13.IssueComment>.empty(), - ) as _i19.Stream<_i13.IssueComment>); - @override - _i19.Future<_i13.IssueComment> getComment( - _i13.RepositorySlug? slug, - int? id, - ) => - (super.noSuchMethod( - Invocation.method( - #getComment, - [ - slug, - id, - ], - ), - returnValue: _i19.Future<_i13.IssueComment>.value(_FakeIssueComment_24( - this, - Invocation.method( - #getComment, - [ - slug, - id, - ], - ), - )), - ) as _i19.Future<_i13.IssueComment>); - @override - _i19.Future<_i13.IssueComment> createComment( - _i13.RepositorySlug? slug, - int? issueNumber, - String? body, - ) => - (super.noSuchMethod( - Invocation.method( - #createComment, - [ - slug, - issueNumber, - body, - ], - ), - returnValue: _i19.Future<_i13.IssueComment>.value(_FakeIssueComment_24( - this, - Invocation.method( - #createComment, - [ - slug, - issueNumber, - body, - ], - ), - )), - ) as _i19.Future<_i13.IssueComment>); - @override - _i19.Future<_i13.IssueComment> updateComment( - _i13.RepositorySlug? slug, - int? id, - String? body, - ) => - (super.noSuchMethod( - Invocation.method( - #updateComment, - [ - slug, - id, - body, - ], - ), - returnValue: _i19.Future<_i13.IssueComment>.value(_FakeIssueComment_24( - this, - Invocation.method( - #updateComment, - [ - slug, - id, - body, - ], - ), - )), - ) as _i19.Future<_i13.IssueComment>); - @override - _i19.Future deleteComment( - _i13.RepositorySlug? slug, - int? id, - ) => - (super.noSuchMethod( - Invocation.method( - #deleteComment, - [ - slug, - id, - ], - ), - returnValue: _i19.Future.value(false), - ) as _i19.Future); - @override - _i19.Stream<_i13.IssueLabel> listLabels(_i13.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listLabels, - [slug], - ), - returnValue: _i19.Stream<_i13.IssueLabel>.empty(), - ) as _i19.Stream<_i13.IssueLabel>); - @override - _i19.Future<_i13.IssueLabel> getLabel( - _i13.RepositorySlug? slug, - String? name, - ) => - (super.noSuchMethod( - Invocation.method( - #getLabel, - [ - slug, - name, - ], - ), - returnValue: _i19.Future<_i13.IssueLabel>.value(_FakeIssueLabel_25( - this, - Invocation.method( - #getLabel, - [ - slug, - name, - ], - ), - )), - ) as _i19.Future<_i13.IssueLabel>); - @override - _i19.Future<_i13.IssueLabel> createLabel( - _i13.RepositorySlug? slug, - String? name, { - String? color, - String? description, - }) => - (super.noSuchMethod( - Invocation.method( - #createLabel, - [ - slug, - name, - ], - { - #color: color, - #description: description, - }, - ), - returnValue: _i19.Future<_i13.IssueLabel>.value(_FakeIssueLabel_25( - this, - Invocation.method( - #createLabel, - [ - slug, - name, - ], - { - #color: color, - #description: description, - }, - ), - )), - ) as _i19.Future<_i13.IssueLabel>); - @override - _i19.Future<_i13.IssueLabel> editLabel( - _i13.RepositorySlug? slug, - String? name, - String? color, - ) => - (super.noSuchMethod( - Invocation.method( - #editLabel, - [ - slug, - name, - color, - ], - ), - returnValue: _i19.Future<_i13.IssueLabel>.value(_FakeIssueLabel_25( - this, - Invocation.method( - #editLabel, - [ - slug, - name, - color, - ], - ), - )), - ) as _i19.Future<_i13.IssueLabel>); - @override - _i19.Future<_i13.IssueLabel> updateLabel( - _i13.RepositorySlug? slug, - String? name, { - String? newName, - String? color, - String? description, - }) => - (super.noSuchMethod( - Invocation.method( - #updateLabel, - [ - slug, - name, - ], - { - #newName: newName, - #color: color, - #description: description, - }, - ), - returnValue: _i19.Future<_i13.IssueLabel>.value(_FakeIssueLabel_25( - this, - Invocation.method( - #updateLabel, - [ - slug, - name, - ], - { - #newName: newName, - #color: color, - #description: description, - }, - ), - )), - ) as _i19.Future<_i13.IssueLabel>); - @override - _i19.Future deleteLabel( - _i13.RepositorySlug? slug, - String? name, - ) => - (super.noSuchMethod( - Invocation.method( - #deleteLabel, - [ - slug, - name, - ], - ), - returnValue: _i19.Future.value(false), - ) as _i19.Future); - @override - _i19.Stream<_i13.IssueLabel> listLabelsByIssue( - _i13.RepositorySlug? slug, - int? issueNumber, - ) => - (super.noSuchMethod( - Invocation.method( - #listLabelsByIssue, - [ - slug, - issueNumber, - ], - ), - returnValue: _i19.Stream<_i13.IssueLabel>.empty(), - ) as _i19.Stream<_i13.IssueLabel>); - @override - _i19.Future> addLabelsToIssue( - _i13.RepositorySlug? slug, - int? issueNumber, - List? labels, - ) => - (super.noSuchMethod( - Invocation.method( - #addLabelsToIssue, - [ - slug, - issueNumber, - labels, - ], - ), - returnValue: _i19.Future>.value(<_i13.IssueLabel>[]), - ) as _i19.Future>); - @override - _i19.Future> replaceLabelsForIssue( - _i13.RepositorySlug? slug, - int? issueNumber, - List? labels, - ) => - (super.noSuchMethod( - Invocation.method( - #replaceLabelsForIssue, - [ - slug, - issueNumber, - labels, - ], - ), - returnValue: _i19.Future>.value(<_i13.IssueLabel>[]), - ) as _i19.Future>); - @override - _i19.Future removeLabelForIssue( - _i13.RepositorySlug? slug, - int? issueNumber, - String? label, - ) => - (super.noSuchMethod( - Invocation.method( - #removeLabelForIssue, - [ - slug, - issueNumber, - label, - ], - ), - returnValue: _i19.Future.value(false), - ) as _i19.Future); - @override - _i19.Future removeAllLabelsForIssue( - _i13.RepositorySlug? slug, - int? issueNumber, - ) => - (super.noSuchMethod( - Invocation.method( - #removeAllLabelsForIssue, - [ - slug, - issueNumber, - ], - ), - returnValue: _i19.Future.value(false), - ) as _i19.Future); - @override - _i19.Stream<_i13.Milestone> listMilestones(_i13.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listMilestones, - [slug], - ), - returnValue: _i19.Stream<_i13.Milestone>.empty(), - ) as _i19.Stream<_i13.Milestone>); - @override - _i19.Future<_i13.Milestone> createMilestone( - _i13.RepositorySlug? slug, - _i13.CreateMilestone? request, - ) => - (super.noSuchMethod( - Invocation.method( - #createMilestone, - [ - slug, - request, - ], - ), - returnValue: _i19.Future<_i13.Milestone>.value(_FakeMilestone_26( - this, - Invocation.method( - #createMilestone, - [ - slug, - request, - ], - ), - )), - ) as _i19.Future<_i13.Milestone>); - @override - _i19.Future deleteMilestone( - _i13.RepositorySlug? slug, - int? number, - ) => - (super.noSuchMethod( - Invocation.method( - #deleteMilestone, - [ - slug, - number, - ], - ), - returnValue: _i19.Future.value(false), - ) as _i19.Future); - @override - _i19.Stream<_i13.TimelineEvent> listTimeline( - _i13.RepositorySlug? slug, - int? issueNumber, - ) => - (super.noSuchMethod( - Invocation.method( - #listTimeline, - [ - slug, - issueNumber, - ], - ), - returnValue: _i19.Stream<_i13.TimelineEvent>.empty(), - ) as _i19.Stream<_i13.TimelineEvent>); - @override - _i19.Future lock( - _i13.RepositorySlug? slug, - int? number, { - String? lockReason, - }) => - (super.noSuchMethod( - Invocation.method( - #lock, - [ - slug, - number, - ], - {#lockReason: lockReason}, - ), - returnValue: _i19.Future.value(), - returnValueForMissingStub: _i19.Future.value(), - ) as _i19.Future); - @override - _i19.Future unlock( - _i13.RepositorySlug? slug, - int? number, - ) => - (super.noSuchMethod( - Invocation.method( - #unlock, - [ - slug, - number, - ], - ), - returnValue: _i19.Future.value(), - returnValueForMissingStub: _i19.Future.value(), - ) as _i19.Future); -} - -/// A class which mocks [GithubChecksService]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockGithubChecksService extends _i1.Mock implements _i23.GithubChecksService { - MockGithubChecksService() { - _i1.throwOnMissingStub(this); - } - - @override - _i3.Config get config => (super.noSuchMethod( - Invocation.getter(#config), - returnValue: _FakeConfig_1( - this, - Invocation.getter(#config), - ), - ) as _i3.Config); - @override - set config(_i3.Config? _config) => super.noSuchMethod( - Invocation.setter( - #config, - _config, - ), - returnValueForMissingStub: null, - ); - @override - _i20.GithubChecksUtil get githubChecksUtil => (super.noSuchMethod( - Invocation.getter(#githubChecksUtil), - returnValue: _FakeGithubChecksUtil_27( - this, - Invocation.getter(#githubChecksUtil), - ), - ) as _i20.GithubChecksUtil); - @override - set githubChecksUtil(_i20.GithubChecksUtil? _githubChecksUtil) => super.noSuchMethod( - Invocation.setter( - #githubChecksUtil, - _githubChecksUtil, - ), - returnValueForMissingStub: null, - ); - @override - _i19.Future handleCheckSuite( - _i13.PullRequest? pullRequest, - _i29.CheckSuiteEvent? checkSuiteEvent, - _i23.Scheduler? scheduler, - ) => - (super.noSuchMethod( - Invocation.method( - #handleCheckSuite, - [ - pullRequest, - checkSuiteEvent, - scheduler, - ], - ), - returnValue: _i19.Future.value(), - returnValueForMissingStub: _i19.Future.value(), - ) as _i19.Future); - @override - _i19.Future updateCheckStatus( - _i36.BuildPushMessage? buildPushMessage, - _i23.LuciBuildService? luciBuildService, - _i13.RepositorySlug? slug, { - bool? rescheduled = false, - }) => - (super.noSuchMethod( - Invocation.method( - #updateCheckStatus, - [ - buildPushMessage, - luciBuildService, - slug, - ], - {#rescheduled: rescheduled}, - ), - returnValue: _i19.Future.value(false), - ) as _i19.Future); - @override - bool taskFailed(_i36.BuildPushMessage? buildPushMessage) => (super.noSuchMethod( - Invocation.method( - #taskFailed, - [buildPushMessage], - ), - returnValue: false, - ) as bool); - @override - int currentAttempt(_i36.BuildPushMessage? buildPushMessage) => (super.noSuchMethod( - Invocation.method( - #currentAttempt, - [buildPushMessage], - ), - returnValue: 0, - ) as int); - @override - String getGithubSummary(String? summary) => (super.noSuchMethod( - Invocation.method( - #getGithubSummary, - [summary], - ), - returnValue: '', - ) as String); - @override - _i13.CheckRunConclusion conclusionForResult(_i36.Result? result) => (super.noSuchMethod( - Invocation.method( - #conclusionForResult, - [result], - ), - returnValue: _FakeCheckRunConclusion_28( - this, - Invocation.method( - #conclusionForResult, - [result], - ), - ), - ) as _i13.CheckRunConclusion); - @override - _i13.CheckRunStatus statusForResult(_i36.Status? status) => (super.noSuchMethod( - Invocation.method( - #statusForResult, - [status], - ), - returnValue: _FakeCheckRunStatus_29( - this, - Invocation.method( - #statusForResult, - [status], - ), - ), - ) as _i13.CheckRunStatus); - @override - _i19.Future<_i13.PullRequest?> findMatchingPullRequest( - _i13.RepositorySlug? slug, - String? headSha, - int? checkSuiteId, - ) => - (super.noSuchMethod( - Invocation.method( - #findMatchingPullRequest, - [ - slug, - headSha, - checkSuiteId, - ], - ), - returnValue: _i19.Future<_i13.PullRequest?>.value(), - ) as _i19.Future<_i13.PullRequest?>); -} - -/// A class which mocks [GithubChecksUtil]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockGithubChecksUtil extends _i1.Mock implements _i20.GithubChecksUtil { - MockGithubChecksUtil() { - _i1.throwOnMissingStub(this); - } - - @override - _i19.Future> allCheckRuns( - _i13.GitHub? gitHubClient, - _i29.CheckSuiteEvent? checkSuiteEvent, - ) => - (super.noSuchMethod( - Invocation.method( - #allCheckRuns, - [ - gitHubClient, - checkSuiteEvent, - ], - ), - returnValue: _i19.Future>.value({}), - ) as _i19.Future>); - @override - _i19.Future<_i13.CheckSuite> getCheckSuite( - _i13.GitHub? gitHubClient, - _i13.RepositorySlug? slug, - int? checkSuiteId, - ) => - (super.noSuchMethod( - Invocation.method( - #getCheckSuite, - [ - gitHubClient, - slug, - checkSuiteId, - ], - ), - returnValue: _i19.Future<_i13.CheckSuite>.value(_FakeCheckSuite_30( - this, - Invocation.method( - #getCheckSuite, - [ - gitHubClient, - slug, - checkSuiteId, - ], - ), - )), - ) as _i19.Future<_i13.CheckSuite>); - @override - _i19.Future> listCheckSuitesForRef( - _i13.GitHub? gitHubClient, - _i13.RepositorySlug? slug, { - required String? ref, - int? appId, - String? checkName, - }) => - (super.noSuchMethod( - Invocation.method( - #listCheckSuitesForRef, - [ - gitHubClient, - slug, - ], - { - #ref: ref, - #appId: appId, - #checkName: checkName, - }, - ), - returnValue: _i19.Future>.value(<_i13.CheckSuite>[]), - ) as _i19.Future>); - @override - _i19.Future updateCheckRun( - _i3.Config? config, - _i13.RepositorySlug? slug, - _i13.CheckRun? checkRun, { - _i13.CheckRunStatus? status = _i13.CheckRunStatus.queued, - _i13.CheckRunConclusion? conclusion, - String? detailsUrl, - _i13.CheckRunOutput? output, - }) => - (super.noSuchMethod( - Invocation.method( - #updateCheckRun, - [ - config, - slug, - checkRun, - ], - { - #status: status, - #conclusion: conclusion, - #detailsUrl: detailsUrl, - #output: output, - }, - ), - returnValue: _i19.Future.value(), - returnValueForMissingStub: _i19.Future.value(), - ) as _i19.Future); - @override - _i19.Future<_i13.CheckRun> getCheckRun( - _i3.Config? config, - _i13.RepositorySlug? slug, - int? id, - ) => - (super.noSuchMethod( - Invocation.method( - #getCheckRun, - [ - config, - slug, - id, - ], - ), - returnValue: _i19.Future<_i13.CheckRun>.value(_FakeCheckRun_31( - this, - Invocation.method( - #getCheckRun, - [ - config, - slug, - id, - ], - ), - )), - ) as _i19.Future<_i13.CheckRun>); - @override - _i19.Future<_i13.CheckRun> createCheckRun( - _i3.Config? config, - _i13.RepositorySlug? slug, - String? sha, - String? name, { - _i13.CheckRunOutput? output, - }) => - (super.noSuchMethod( - Invocation.method( - #createCheckRun, - [ - config, - slug, - sha, - name, - ], - {#output: output}, - ), - returnValue: _i19.Future<_i13.CheckRun>.value(_FakeCheckRun_31( - this, - Invocation.method( - #createCheckRun, - [ - config, - slug, - sha, - name, - ], - {#output: output}, - ), - )), - ) as _i19.Future<_i13.CheckRun>); -} - -/// A class which mocks [GithubService]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockGithubService extends _i1.Mock implements _i16.GithubService { - MockGithubService() { - _i1.throwOnMissingStub(this); - } - - @override - _i13.GitHub get github => (super.noSuchMethod( - Invocation.getter(#github), - returnValue: _FakeGitHub_16( - this, - Invocation.getter(#github), - ), - ) as _i13.GitHub); - @override - _i19.Future> listBranchedCommits( - _i13.RepositorySlug? slug, - String? branch, - int? lastCommitTimestampMills, - ) => - (super.noSuchMethod( - Invocation.method( - #listBranchedCommits, - [ - slug, - branch, - lastCommitTimestampMills, - ], - ), - returnValue: _i19.Future>.value(<_i13.RepositoryCommit>[]), - ) as _i19.Future>); - @override - _i19.Future> listPullRequests( - _i13.RepositorySlug? slug, - String? branch, - ) => - (super.noSuchMethod( - Invocation.method( - #listPullRequests, - [ - slug, - branch, - ], - ), - returnValue: _i19.Future>.value(<_i13.PullRequest>[]), - ) as _i19.Future>); - @override - _i19.Future<_i13.PullRequest> createPullRequest( - _i13.RepositorySlug? slug, { - required String? title, - String? body, - String? commitMessage, - required _i13.GitReference? baseRef, - List<_i13.CreateGitTreeEntry>? entries, - }) => - (super.noSuchMethod( - Invocation.method( - #createPullRequest, - [slug], - { - #title: title, - #body: body, - #commitMessage: commitMessage, - #baseRef: baseRef, - #entries: entries, - }, - ), - returnValue: _i19.Future<_i13.PullRequest>.value(_FakePullRequest_32( - this, - Invocation.method( - #createPullRequest, - [slug], - { - #title: title, - #body: body, - #commitMessage: commitMessage, - #baseRef: baseRef, - #entries: entries, - }, - ), - )), - ) as _i19.Future<_i13.PullRequest>); - @override - _i19.Future assignReviewer( - _i13.RepositorySlug? slug, { - int? pullRequestNumber, - String? reviewer, - }) => - (super.noSuchMethod( - Invocation.method( - #assignReviewer, - [slug], - { - #pullRequestNumber: pullRequestNumber, - #reviewer: reviewer, - }, - ), - returnValue: _i19.Future.value(), - returnValueForMissingStub: _i19.Future.value(), - ) as _i19.Future); - @override - _i19.Future> addIssueLabels( - _i13.RepositorySlug? slug, - int? issueNumber, - List? labels, - ) => - (super.noSuchMethod( - Invocation.method( - #addIssueLabels, - [ - slug, - issueNumber, - labels, - ], - ), - returnValue: _i19.Future>.value(<_i13.IssueLabel>[]), - ) as _i19.Future>); - @override - _i19.Future> listIssues( - _i13.RepositorySlug? slug, { - List? labels, - String? state = r'open', - }) => - (super.noSuchMethod( - Invocation.method( - #listIssues, - [slug], - { - #labels: labels, - #state: state, - }, - ), - returnValue: _i19.Future>.value(<_i13.Issue>[]), - ) as _i19.Future>); - @override - _i19.Future<_i13.Issue>? getIssue( - _i13.RepositorySlug? slug, { - required int? issueNumber, - }) => - (super.noSuchMethod(Invocation.method( - #getIssue, - [slug], - {#issueNumber: issueNumber}, - )) as _i19.Future<_i13.Issue>?); - @override - _i19.Future assignIssue( - _i13.RepositorySlug? slug, { - required int? issueNumber, - required String? assignee, - }) => - (super.noSuchMethod( - Invocation.method( - #assignIssue, - [slug], - { - #issueNumber: issueNumber, - #assignee: assignee, - }, - ), - returnValue: _i19.Future.value(), - returnValueForMissingStub: _i19.Future.value(), - ) as _i19.Future); - @override - _i19.Future<_i13.Issue> createIssue( - _i13.RepositorySlug? slug, { - String? title, - String? body, - List? labels, - String? assignee, - }) => - (super.noSuchMethod( - Invocation.method( - #createIssue, - [slug], - { - #title: title, - #body: body, - #labels: labels, - #assignee: assignee, - }, - ), - returnValue: _i19.Future<_i13.Issue>.value(_FakeIssue_23( - this, - Invocation.method( - #createIssue, - [slug], - { - #title: title, - #body: body, - #labels: labels, - #assignee: assignee, - }, - ), - )), - ) as _i19.Future<_i13.Issue>); - @override - _i19.Future<_i13.IssueComment?> createComment( - _i13.RepositorySlug? slug, { - required int? issueNumber, - required String? body, - }) => - (super.noSuchMethod( - Invocation.method( - #createComment, - [slug], - { - #issueNumber: issueNumber, - #body: body, - }, - ), - returnValue: _i19.Future<_i13.IssueComment?>.value(), - ) as _i19.Future<_i13.IssueComment?>); - @override - _i19.Future> replaceLabelsForIssue( - _i13.RepositorySlug? slug, { - required int? issueNumber, - required List? labels, - }) => - (super.noSuchMethod( - Invocation.method( - #replaceLabelsForIssue, - [slug], - { - #issueNumber: issueNumber, - #labels: labels, - }, - ), - returnValue: _i19.Future>.value(<_i13.IssueLabel>[]), - ) as _i19.Future>); - @override - _i19.Future> listFiles(_i13.PullRequest? pullRequest) => (super.noSuchMethod( - Invocation.method( - #listFiles, - [pullRequest], - ), - returnValue: _i19.Future>.value([]), - ) as _i19.Future>); - @override - _i19.Future getFileContent( - _i13.RepositorySlug? slug, - String? path, { - String? ref, - }) => - (super.noSuchMethod( - Invocation.method( - #getFileContent, - [ - slug, - path, - ], - {#ref: ref}, - ), - returnValue: _i19.Future.value(''), - ) as _i19.Future); - @override - _i19.Future<_i13.GitReference> getReference( - _i13.RepositorySlug? slug, - String? ref, - ) => - (super.noSuchMethod( - Invocation.method( - #getReference, - [ - slug, - ref, - ], - ), - returnValue: _i19.Future<_i13.GitReference>.value(_FakeGitReference_33( - this, - Invocation.method( - #getReference, - [ - slug, - ref, - ], - ), - )), - ) as _i19.Future<_i13.GitReference>); - @override - _i19.Future<_i13.RateLimit> getRateLimit() => (super.noSuchMethod( - Invocation.method( - #getRateLimit, - [], - ), - returnValue: _i19.Future<_i13.RateLimit>.value(_FakeRateLimit_34( - this, - Invocation.method( - #getRateLimit, - [], - ), - )), - ) as _i19.Future<_i13.RateLimit>); - @override - _i19.Future> searchIssuesAndPRs( - _i13.RepositorySlug? slug, - String? query, { - String? sort, - int? pages = 2, - }) => - (super.noSuchMethod( - Invocation.method( - #searchIssuesAndPRs, - [ - slug, - query, - ], - { - #sort: sort, - #pages: pages, - }, - ), - returnValue: _i19.Future>.value(<_i13.Issue>[]), - ) as _i19.Future>); - @override - _i19.Future<_i13.PullRequest> getPullRequest( - _i13.RepositorySlug? slug, - int? number, - ) => - (super.noSuchMethod( - Invocation.method( - #getPullRequest, - [ - slug, - number, - ], - ), - returnValue: _i19.Future<_i13.PullRequest>.value(_FakePullRequest_32( - this, - Invocation.method( - #getPullRequest, - [ - slug, - number, - ], - ), - )), - ) as _i19.Future<_i13.PullRequest>); -} - -/// A class which mocks [GitService]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockGitService extends _i1.Mock implements _i13.GitService { - MockGitService() { - _i1.throwOnMissingStub(this); - } - - @override - _i13.GitHub get github => (super.noSuchMethod( - Invocation.getter(#github), - returnValue: _FakeGitHub_16( - this, - Invocation.getter(#github), - ), - ) as _i13.GitHub); - @override - _i19.Future<_i13.GitBlob> getBlob( - _i13.RepositorySlug? slug, - String? sha, - ) => - (super.noSuchMethod( - Invocation.method( - #getBlob, - [ - slug, - sha, - ], - ), - returnValue: _i19.Future<_i13.GitBlob>.value(_FakeGitBlob_35( - this, - Invocation.method( - #getBlob, - [ - slug, - sha, - ], - ), - )), - ) as _i19.Future<_i13.GitBlob>); - @override - _i19.Future<_i13.GitBlob> createBlob( - _i13.RepositorySlug? slug, - _i13.CreateGitBlob? blob, - ) => - (super.noSuchMethod( - Invocation.method( - #createBlob, - [ - slug, - blob, - ], - ), - returnValue: _i19.Future<_i13.GitBlob>.value(_FakeGitBlob_35( - this, - Invocation.method( - #createBlob, - [ - slug, - blob, - ], - ), - )), - ) as _i19.Future<_i13.GitBlob>); - @override - _i19.Future<_i13.GitCommit> getCommit( - _i13.RepositorySlug? slug, - String? sha, - ) => - (super.noSuchMethod( - Invocation.method( - #getCommit, - [ - slug, - sha, - ], - ), - returnValue: _i19.Future<_i13.GitCommit>.value(_FakeGitCommit_36( - this, - Invocation.method( - #getCommit, - [ - slug, - sha, - ], - ), - )), - ) as _i19.Future<_i13.GitCommit>); - @override - _i19.Future<_i13.GitCommit> createCommit( - _i13.RepositorySlug? slug, - _i13.CreateGitCommit? commit, - ) => - (super.noSuchMethod( - Invocation.method( - #createCommit, - [ - slug, - commit, - ], - ), - returnValue: _i19.Future<_i13.GitCommit>.value(_FakeGitCommit_36( - this, - Invocation.method( - #createCommit, - [ - slug, - commit, - ], - ), - )), - ) as _i19.Future<_i13.GitCommit>); - @override - _i19.Future<_i13.GitReference> getReference( - _i13.RepositorySlug? slug, - String? ref, - ) => - (super.noSuchMethod( - Invocation.method( - #getReference, - [ - slug, - ref, - ], - ), - returnValue: _i19.Future<_i13.GitReference>.value(_FakeGitReference_33( - this, - Invocation.method( - #getReference, - [ - slug, - ref, - ], - ), - )), - ) as _i19.Future<_i13.GitReference>); - @override - _i19.Stream<_i13.GitReference> listReferences( - _i13.RepositorySlug? slug, { - String? type, - }) => - (super.noSuchMethod( - Invocation.method( - #listReferences, - [slug], - {#type: type}, - ), - returnValue: _i19.Stream<_i13.GitReference>.empty(), - ) as _i19.Stream<_i13.GitReference>); - @override - _i19.Future<_i13.GitReference> createReference( - _i13.RepositorySlug? slug, - String? ref, - String? sha, - ) => - (super.noSuchMethod( - Invocation.method( - #createReference, - [ - slug, - ref, - sha, - ], - ), - returnValue: _i19.Future<_i13.GitReference>.value(_FakeGitReference_33( - this, - Invocation.method( - #createReference, - [ - slug, - ref, - sha, - ], - ), - )), - ) as _i19.Future<_i13.GitReference>); - @override - _i19.Future<_i13.GitReference> editReference( - _i13.RepositorySlug? slug, - String? ref, - String? sha, { - bool? force = false, - }) => - (super.noSuchMethod( - Invocation.method( - #editReference, - [ - slug, - ref, - sha, - ], - {#force: force}, - ), - returnValue: _i19.Future<_i13.GitReference>.value(_FakeGitReference_33( - this, - Invocation.method( - #editReference, - [ - slug, - ref, - sha, - ], - {#force: force}, - ), - )), - ) as _i19.Future<_i13.GitReference>); - @override - _i19.Future deleteReference( - _i13.RepositorySlug? slug, - String? ref, - ) => - (super.noSuchMethod( - Invocation.method( - #deleteReference, - [ - slug, - ref, - ], - ), - returnValue: _i19.Future.value(false), - ) as _i19.Future); - @override - _i19.Future<_i13.GitTag> getTag( - _i13.RepositorySlug? slug, - String? sha, - ) => - (super.noSuchMethod( - Invocation.method( - #getTag, - [ - slug, - sha, - ], - ), - returnValue: _i19.Future<_i13.GitTag>.value(_FakeGitTag_37( - this, - Invocation.method( - #getTag, - [ - slug, - sha, - ], - ), - )), - ) as _i19.Future<_i13.GitTag>); - @override - _i19.Future<_i13.GitTag> createTag( - _i13.RepositorySlug? slug, - _i13.CreateGitTag? tag, - ) => - (super.noSuchMethod( - Invocation.method( - #createTag, - [ - slug, - tag, - ], - ), - returnValue: _i19.Future<_i13.GitTag>.value(_FakeGitTag_37( - this, - Invocation.method( - #createTag, - [ - slug, - tag, - ], - ), - )), - ) as _i19.Future<_i13.GitTag>); - @override - _i19.Future<_i13.GitTree> getTree( - _i13.RepositorySlug? slug, - String? sha, { - bool? recursive = false, - }) => - (super.noSuchMethod( - Invocation.method( - #getTree, - [ - slug, - sha, - ], - {#recursive: recursive}, - ), - returnValue: _i19.Future<_i13.GitTree>.value(_FakeGitTree_38( - this, - Invocation.method( - #getTree, - [ - slug, - sha, - ], - {#recursive: recursive}, - ), - )), - ) as _i19.Future<_i13.GitTree>); - @override - _i19.Future<_i13.GitTree> createTree( - _i13.RepositorySlug? slug, - _i13.CreateGitTree? tree, - ) => - (super.noSuchMethod( - Invocation.method( - #createTree, - [ - slug, - tree, - ], - ), - returnValue: _i19.Future<_i13.GitTree>.value(_FakeGitTree_38( - this, - Invocation.method( - #createTree, - [ - slug, - tree, - ], - ), - )), - ) as _i19.Future<_i13.GitTree>); -} - -/// A class which mocks [GraphQLClient]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockGraphQLClient extends _i1.Mock implements _i14.GraphQLClient { - MockGraphQLClient() { - _i1.throwOnMissingStub(this); - } - - @override - _i14.DefaultPolicies get defaultPolicies => (super.noSuchMethod( - Invocation.getter(#defaultPolicies), - returnValue: _FakeDefaultPolicies_39( - this, - Invocation.getter(#defaultPolicies), - ), - ) as _i14.DefaultPolicies); - @override - set defaultPolicies(_i14.DefaultPolicies? _defaultPolicies) => super.noSuchMethod( - Invocation.setter( - #defaultPolicies, - _defaultPolicies, - ), - returnValueForMissingStub: null, - ); - @override - _i14.Link get link => (super.noSuchMethod( - Invocation.getter(#link), - returnValue: _FakeLink_40( - this, - Invocation.getter(#link), - ), - ) as _i14.Link); - @override - _i14.GraphQLCache get cache => (super.noSuchMethod( - Invocation.getter(#cache), - returnValue: _FakeGraphQLCache_41( - this, - Invocation.getter(#cache), - ), - ) as _i14.GraphQLCache); - @override - _i14.QueryManager get queryManager => (super.noSuchMethod( - Invocation.getter(#queryManager), - returnValue: _FakeQueryManager_42( - this, - Invocation.getter(#queryManager), - ), - ) as _i14.QueryManager); - @override - set queryManager(_i14.QueryManager? _queryManager) => super.noSuchMethod( - Invocation.setter( - #queryManager, - _queryManager, - ), - returnValueForMissingStub: null, - ); - @override - _i14.GraphQLClient copyWith({ - _i14.Link? link, - _i14.GraphQLCache? cache, - _i14.DefaultPolicies? defaultPolicies, - bool? alwaysRebroadcast, - }) => - (super.noSuchMethod( - Invocation.method( - #copyWith, - [], - { - #link: link, - #cache: cache, - #defaultPolicies: defaultPolicies, - #alwaysRebroadcast: alwaysRebroadcast, - }, - ), - returnValue: _FakeGraphQLClient_17( - this, - Invocation.method( - #copyWith, - [], - { - #link: link, - #cache: cache, - #defaultPolicies: defaultPolicies, - #alwaysRebroadcast: alwaysRebroadcast, - }, - ), - ), - ) as _i14.GraphQLClient); - @override - _i14.ObservableQuery watchQuery(_i14.WatchQueryOptions? options) => (super.noSuchMethod( - Invocation.method( - #watchQuery, - [options], - ), - returnValue: _FakeObservableQuery_43( - this, - Invocation.method( - #watchQuery, - [options], - ), - ), - ) as _i14.ObservableQuery); - @override - _i14.ObservableQuery watchMutation(_i14.WatchQueryOptions? options) => (super.noSuchMethod( - Invocation.method( - #watchMutation, - [options], - ), - returnValue: _FakeObservableQuery_43( - this, - Invocation.method( - #watchMutation, - [options], - ), - ), - ) as _i14.ObservableQuery); - @override - _i19.Future<_i14.QueryResult> query(_i14.QueryOptions? options) => (super.noSuchMethod( - Invocation.method( - #query, - [options], - ), - returnValue: _i19.Future<_i14.QueryResult>.value(_FakeQueryResult_44( - this, - Invocation.method( - #query, - [options], - ), - )), - ) as _i19.Future<_i14.QueryResult>); - @override - _i19.Future<_i14.QueryResult> mutate(_i14.MutationOptions? options) => (super.noSuchMethod( - Invocation.method( - #mutate, - [options], - ), - returnValue: _i19.Future<_i14.QueryResult>.value(_FakeQueryResult_44( - this, - Invocation.method( - #mutate, - [options], - ), - )), - ) as _i19.Future<_i14.QueryResult>); - @override - _i19.Stream<_i14.QueryResult> subscribe(_i14.SubscriptionOptions? options) => - (super.noSuchMethod( - Invocation.method( - #subscribe, - [options], - ), - returnValue: _i19.Stream<_i14.QueryResult>.empty(), - ) as _i19.Stream<_i14.QueryResult>); - @override - _i19.Future<_i14.QueryResult> fetchMore( - _i14.FetchMoreOptions? fetchMoreOptions, { - required _i14.QueryOptions? originalOptions, - required _i14.QueryResult? previousResult, - }) => - (super.noSuchMethod( - Invocation.method( - #fetchMore, - [fetchMoreOptions], - { - #originalOptions: originalOptions, - #previousResult: previousResult, - }, - ), - returnValue: _i19.Future<_i14.QueryResult>.value(_FakeQueryResult_44( - this, - Invocation.method( - #fetchMore, - [fetchMoreOptions], - { - #originalOptions: originalOptions, - #previousResult: previousResult, - }, - ), - )), - ) as _i19.Future<_i14.QueryResult>); - @override - Map? readQuery( - _i14.Request? request, { - bool? optimistic = true, - }) => - (super.noSuchMethod(Invocation.method( - #readQuery, - [request], - {#optimistic: optimistic}, - )) as Map?); - @override - Map? readFragment( - _i14.FragmentRequest? fragmentRequest, { - bool? optimistic = true, - }) => - (super.noSuchMethod(Invocation.method( - #readFragment, - [fragmentRequest], - {#optimistic: optimistic}, - )) as Map?); - @override - void writeQuery( - _i14.Request? request, { - required Map? data, - bool? broadcast = true, - }) => - super.noSuchMethod( - Invocation.method( - #writeQuery, - [request], - { - #data: data, - #broadcast: broadcast, - }, - ), - returnValueForMissingStub: null, - ); - @override - void writeFragment( - _i14.FragmentRequest? fragmentRequest, { - bool? broadcast = true, - required Map? data, - }) => - super.noSuchMethod( - Invocation.method( - #writeFragment, - [fragmentRequest], - { - #broadcast: broadcast, - #data: data, - }, - ), - returnValueForMissingStub: null, - ); - @override - _i19.Future?>>? resetStore({bool? refetchQueries = true}) => - (super.noSuchMethod(Invocation.method( - #resetStore, - [], - {#refetchQueries: refetchQueries}, - )) as _i19.Future?>>?); -} - -/// A class which mocks [HttpClient]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockHttpClient extends _i1.Mock implements _i21.HttpClient { - MockHttpClient() { - _i1.throwOnMissingStub(this); - } - - @override - Duration get idleTimeout => (super.noSuchMethod( - Invocation.getter(#idleTimeout), - returnValue: _FakeDuration_15( - this, - Invocation.getter(#idleTimeout), - ), - ) as Duration); - @override - set idleTimeout(Duration? _idleTimeout) => super.noSuchMethod( - Invocation.setter( - #idleTimeout, - _idleTimeout, - ), - returnValueForMissingStub: null, - ); - @override - set connectionTimeout(Duration? _connectionTimeout) => super.noSuchMethod( - Invocation.setter( - #connectionTimeout, - _connectionTimeout, - ), - returnValueForMissingStub: null, - ); - @override - set maxConnectionsPerHost(int? _maxConnectionsPerHost) => super.noSuchMethod( - Invocation.setter( - #maxConnectionsPerHost, - _maxConnectionsPerHost, - ), - returnValueForMissingStub: null, - ); - @override - bool get autoUncompress => (super.noSuchMethod( - Invocation.getter(#autoUncompress), - returnValue: false, - ) as bool); - @override - set autoUncompress(bool? _autoUncompress) => super.noSuchMethod( - Invocation.setter( - #autoUncompress, - _autoUncompress, - ), - returnValueForMissingStub: null, - ); - @override - set userAgent(String? _userAgent) => super.noSuchMethod( - Invocation.setter( - #userAgent, - _userAgent, - ), - returnValueForMissingStub: null, - ); - @override - set authenticate( - _i19.Future Function( - Uri, - String, - String?, - )? f) => - super.noSuchMethod( - Invocation.setter( - #authenticate, - f, - ), - returnValueForMissingStub: null, - ); - @override - set connectionFactory( - _i19.Future<_i21.ConnectionTask<_i21.Socket>> Function( - Uri, - String?, - int?, - )? f) => - super.noSuchMethod( - Invocation.setter( - #connectionFactory, - f, - ), - returnValueForMissingStub: null, - ); - @override - set findProxy(String Function(Uri)? f) => super.noSuchMethod( - Invocation.setter( - #findProxy, - f, - ), - returnValueForMissingStub: null, - ); - @override - set authenticateProxy( - _i19.Future Function( - String, - int, - String, - String?, - )? f) => - super.noSuchMethod( - Invocation.setter( - #authenticateProxy, - f, - ), - returnValueForMissingStub: null, - ); - @override - set badCertificateCallback( - bool Function( - _i21.X509Certificate, - String, - int, - )? callback) => - super.noSuchMethod( - Invocation.setter( - #badCertificateCallback, - callback, - ), - returnValueForMissingStub: null, - ); - @override - set keyLog(dynamic Function(String)? callback) => super.noSuchMethod( - Invocation.setter( - #keyLog, - callback, - ), - returnValueForMissingStub: null, - ); - @override - _i19.Future<_i21.HttpClientRequest> open( - String? method, - String? host, - int? port, - String? path, - ) => - (super.noSuchMethod( - Invocation.method( - #open, - [ - method, - host, - port, - path, - ], - ), - returnValue: _i19.Future<_i21.HttpClientRequest>.value(_FakeHttpClientRequest_45( - this, - Invocation.method( - #open, - [ - method, - host, - port, - path, - ], - ), - )), - ) as _i19.Future<_i21.HttpClientRequest>); - @override - _i19.Future<_i21.HttpClientRequest> openUrl( - String? method, - Uri? url, - ) => - (super.noSuchMethod( - Invocation.method( - #openUrl, - [ - method, - url, - ], - ), - returnValue: _i19.Future<_i21.HttpClientRequest>.value(_FakeHttpClientRequest_45( - this, - Invocation.method( - #openUrl, - [ - method, - url, - ], - ), - )), - ) as _i19.Future<_i21.HttpClientRequest>); - @override - _i19.Future<_i21.HttpClientRequest> get( - String? host, - int? port, - String? path, - ) => - (super.noSuchMethod( - Invocation.method( - #get, - [ - host, - port, - path, - ], - ), - returnValue: _i19.Future<_i21.HttpClientRequest>.value(_FakeHttpClientRequest_45( - this, - Invocation.method( - #get, - [ - host, - port, - path, - ], - ), - )), - ) as _i19.Future<_i21.HttpClientRequest>); - @override - _i19.Future<_i21.HttpClientRequest> getUrl(Uri? url) => (super.noSuchMethod( - Invocation.method( - #getUrl, - [url], - ), - returnValue: _i19.Future<_i21.HttpClientRequest>.value(_FakeHttpClientRequest_45( - this, - Invocation.method( - #getUrl, - [url], - ), - )), - ) as _i19.Future<_i21.HttpClientRequest>); - @override - _i19.Future<_i21.HttpClientRequest> post( - String? host, - int? port, - String? path, - ) => - (super.noSuchMethod( - Invocation.method( - #post, - [ - host, - port, - path, - ], - ), - returnValue: _i19.Future<_i21.HttpClientRequest>.value(_FakeHttpClientRequest_45( - this, - Invocation.method( - #post, - [ - host, - port, - path, - ], - ), - )), - ) as _i19.Future<_i21.HttpClientRequest>); - @override - _i19.Future<_i21.HttpClientRequest> postUrl(Uri? url) => (super.noSuchMethod( - Invocation.method( - #postUrl, - [url], - ), - returnValue: _i19.Future<_i21.HttpClientRequest>.value(_FakeHttpClientRequest_45( - this, - Invocation.method( - #postUrl, - [url], - ), - )), - ) as _i19.Future<_i21.HttpClientRequest>); - @override - _i19.Future<_i21.HttpClientRequest> put( - String? host, - int? port, - String? path, - ) => - (super.noSuchMethod( - Invocation.method( - #put, - [ - host, - port, - path, - ], - ), - returnValue: _i19.Future<_i21.HttpClientRequest>.value(_FakeHttpClientRequest_45( - this, - Invocation.method( - #put, - [ - host, - port, - path, - ], - ), - )), - ) as _i19.Future<_i21.HttpClientRequest>); - @override - _i19.Future<_i21.HttpClientRequest> putUrl(Uri? url) => (super.noSuchMethod( - Invocation.method( - #putUrl, - [url], - ), - returnValue: _i19.Future<_i21.HttpClientRequest>.value(_FakeHttpClientRequest_45( - this, - Invocation.method( - #putUrl, - [url], - ), - )), - ) as _i19.Future<_i21.HttpClientRequest>); - @override - _i19.Future<_i21.HttpClientRequest> delete( - String? host, - int? port, - String? path, - ) => - (super.noSuchMethod( - Invocation.method( - #delete, - [ - host, - port, - path, - ], - ), - returnValue: _i19.Future<_i21.HttpClientRequest>.value(_FakeHttpClientRequest_45( - this, - Invocation.method( - #delete, - [ - host, - port, - path, - ], - ), - )), - ) as _i19.Future<_i21.HttpClientRequest>); - @override - _i19.Future<_i21.HttpClientRequest> deleteUrl(Uri? url) => (super.noSuchMethod( - Invocation.method( - #deleteUrl, - [url], - ), - returnValue: _i19.Future<_i21.HttpClientRequest>.value(_FakeHttpClientRequest_45( - this, - Invocation.method( - #deleteUrl, - [url], - ), - )), - ) as _i19.Future<_i21.HttpClientRequest>); - @override - _i19.Future<_i21.HttpClientRequest> patch( - String? host, - int? port, - String? path, - ) => - (super.noSuchMethod( - Invocation.method( - #patch, - [ - host, - port, - path, - ], - ), - returnValue: _i19.Future<_i21.HttpClientRequest>.value(_FakeHttpClientRequest_45( - this, - Invocation.method( - #patch, - [ - host, - port, - path, - ], - ), - )), - ) as _i19.Future<_i21.HttpClientRequest>); - @override - _i19.Future<_i21.HttpClientRequest> patchUrl(Uri? url) => (super.noSuchMethod( - Invocation.method( - #patchUrl, - [url], - ), - returnValue: _i19.Future<_i21.HttpClientRequest>.value(_FakeHttpClientRequest_45( - this, - Invocation.method( - #patchUrl, - [url], - ), - )), - ) as _i19.Future<_i21.HttpClientRequest>); - @override - _i19.Future<_i21.HttpClientRequest> head( - String? host, - int? port, - String? path, - ) => - (super.noSuchMethod( - Invocation.method( - #head, - [ - host, - port, - path, - ], - ), - returnValue: _i19.Future<_i21.HttpClientRequest>.value(_FakeHttpClientRequest_45( - this, - Invocation.method( - #head, - [ - host, - port, - path, - ], - ), - )), - ) as _i19.Future<_i21.HttpClientRequest>); - @override - _i19.Future<_i21.HttpClientRequest> headUrl(Uri? url) => (super.noSuchMethod( - Invocation.method( - #headUrl, - [url], - ), - returnValue: _i19.Future<_i21.HttpClientRequest>.value(_FakeHttpClientRequest_45( - this, - Invocation.method( - #headUrl, - [url], - ), - )), - ) as _i19.Future<_i21.HttpClientRequest>); - @override - void addCredentials( - Uri? url, - String? realm, - _i21.HttpClientCredentials? credentials, - ) => - super.noSuchMethod( - Invocation.method( - #addCredentials, - [ - url, - realm, - credentials, - ], - ), - returnValueForMissingStub: null, - ); - @override - void addProxyCredentials( - String? host, - int? port, - String? realm, - _i21.HttpClientCredentials? credentials, - ) => - super.noSuchMethod( - Invocation.method( - #addProxyCredentials, - [ - host, - port, - realm, - credentials, - ], - ), - returnValueForMissingStub: null, - ); - @override - void close({bool? force = false}) => super.noSuchMethod( - Invocation.method( - #close, - [], - {#force: force}, - ), - returnValueForMissingStub: null, - ); -} - -/// A class which mocks [HttpClientRequest]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockHttpClientRequest extends _i1.Mock implements _i21.HttpClientRequest { - MockHttpClientRequest() { - _i1.throwOnMissingStub(this); - } - - @override - bool get persistentConnection => (super.noSuchMethod( - Invocation.getter(#persistentConnection), - returnValue: false, - ) as bool); - @override - set persistentConnection(bool? _persistentConnection) => super.noSuchMethod( - Invocation.setter( - #persistentConnection, - _persistentConnection, - ), - returnValueForMissingStub: null, - ); - @override - bool get followRedirects => (super.noSuchMethod( - Invocation.getter(#followRedirects), - returnValue: false, - ) as bool); - @override - set followRedirects(bool? _followRedirects) => super.noSuchMethod( - Invocation.setter( - #followRedirects, - _followRedirects, - ), - returnValueForMissingStub: null, - ); - @override - int get maxRedirects => (super.noSuchMethod( - Invocation.getter(#maxRedirects), - returnValue: 0, - ) as int); - @override - set maxRedirects(int? _maxRedirects) => super.noSuchMethod( - Invocation.setter( - #maxRedirects, - _maxRedirects, - ), - returnValueForMissingStub: null, - ); - @override - int get contentLength => (super.noSuchMethod( - Invocation.getter(#contentLength), - returnValue: 0, - ) as int); - @override - set contentLength(int? _contentLength) => super.noSuchMethod( - Invocation.setter( - #contentLength, - _contentLength, - ), - returnValueForMissingStub: null, - ); - @override - bool get bufferOutput => (super.noSuchMethod( - Invocation.getter(#bufferOutput), - returnValue: false, - ) as bool); - @override - set bufferOutput(bool? _bufferOutput) => super.noSuchMethod( - Invocation.setter( - #bufferOutput, - _bufferOutput, - ), - returnValueForMissingStub: null, - ); - @override - String get method => (super.noSuchMethod( - Invocation.getter(#method), - returnValue: '', - ) as String); - @override - Uri get uri => (super.noSuchMethod( - Invocation.getter(#uri), - returnValue: _FakeUri_46( - this, - Invocation.getter(#uri), - ), - ) as Uri); - @override - _i21.HttpHeaders get headers => (super.noSuchMethod( - Invocation.getter(#headers), - returnValue: _FakeHttpHeaders_47( - this, - Invocation.getter(#headers), - ), - ) as _i21.HttpHeaders); - @override - List<_i21.Cookie> get cookies => (super.noSuchMethod( - Invocation.getter(#cookies), - returnValue: <_i21.Cookie>[], - ) as List<_i21.Cookie>); - @override - _i19.Future<_i21.HttpClientResponse> get done => (super.noSuchMethod( - Invocation.getter(#done), - returnValue: _i19.Future<_i21.HttpClientResponse>.value(_FakeHttpClientResponse_48( - this, - Invocation.getter(#done), - )), - ) as _i19.Future<_i21.HttpClientResponse>); - @override - _i22.Encoding get encoding => (super.noSuchMethod( - Invocation.getter(#encoding), - returnValue: _FakeEncoding_49( - this, - Invocation.getter(#encoding), - ), - ) as _i22.Encoding); - @override - set encoding(_i22.Encoding? _encoding) => super.noSuchMethod( - Invocation.setter( - #encoding, - _encoding, - ), - returnValueForMissingStub: null, - ); - @override - _i19.Future<_i21.HttpClientResponse> close() => (super.noSuchMethod( - Invocation.method( - #close, - [], - ), - returnValue: _i19.Future<_i21.HttpClientResponse>.value(_FakeHttpClientResponse_48( - this, - Invocation.method( - #close, - [], - ), - )), - ) as _i19.Future<_i21.HttpClientResponse>); - @override - void abort([ - Object? exception, - StackTrace? stackTrace, - ]) => - super.noSuchMethod( - Invocation.method( - #abort, - [ - exception, - stackTrace, - ], - ), - returnValueForMissingStub: null, - ); - @override - void add(List? data) => super.noSuchMethod( - Invocation.method( - #add, - [data], - ), - returnValueForMissingStub: null, - ); - @override - void write(Object? object) => super.noSuchMethod( - Invocation.method( - #write, - [object], - ), - returnValueForMissingStub: null, - ); - @override - void writeAll( - Iterable? objects, [ - String? separator = r'', - ]) => - super.noSuchMethod( - Invocation.method( - #writeAll, - [ - objects, - separator, - ], - ), - returnValueForMissingStub: null, - ); - @override - void writeln([Object? object = r'']) => super.noSuchMethod( - Invocation.method( - #writeln, - [object], - ), - returnValueForMissingStub: null, - ); - @override - void writeCharCode(int? charCode) => super.noSuchMethod( - Invocation.method( - #writeCharCode, - [charCode], - ), - returnValueForMissingStub: null, - ); - @override - void addError( - Object? error, [ - StackTrace? stackTrace, - ]) => - super.noSuchMethod( - Invocation.method( - #addError, - [ - error, - stackTrace, - ], - ), - returnValueForMissingStub: null, - ); - @override - _i19.Future addStream(_i19.Stream>? stream) => (super.noSuchMethod( - Invocation.method( - #addStream, - [stream], - ), - returnValue: _i19.Future.value(), - ) as _i19.Future); - @override - _i19.Future flush() => (super.noSuchMethod( - Invocation.method( - #flush, - [], - ), - returnValue: _i19.Future.value(), - ) as _i19.Future); -} - -/// A class which mocks [HttpClientResponse]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockHttpClientResponse extends _i1.Mock implements _i21.HttpClientResponse { - MockHttpClientResponse() { - _i1.throwOnMissingStub(this); - } - - @override - int get statusCode => (super.noSuchMethod( - Invocation.getter(#statusCode), - returnValue: 0, - ) as int); - @override - String get reasonPhrase => (super.noSuchMethod( - Invocation.getter(#reasonPhrase), - returnValue: '', - ) as String); - @override - int get contentLength => (super.noSuchMethod( - Invocation.getter(#contentLength), - returnValue: 0, - ) as int); - @override - _i21.HttpClientResponseCompressionState get compressionState => (super.noSuchMethod( - Invocation.getter(#compressionState), - returnValue: _i21.HttpClientResponseCompressionState.notCompressed, - ) as _i21.HttpClientResponseCompressionState); - @override - bool get persistentConnection => (super.noSuchMethod( - Invocation.getter(#persistentConnection), - returnValue: false, - ) as bool); - @override - bool get isRedirect => (super.noSuchMethod( - Invocation.getter(#isRedirect), - returnValue: false, - ) as bool); - @override - List<_i21.RedirectInfo> get redirects => (super.noSuchMethod( - Invocation.getter(#redirects), - returnValue: <_i21.RedirectInfo>[], - ) as List<_i21.RedirectInfo>); - @override - _i21.HttpHeaders get headers => (super.noSuchMethod( - Invocation.getter(#headers), - returnValue: _FakeHttpHeaders_47( - this, - Invocation.getter(#headers), - ), - ) as _i21.HttpHeaders); - @override - List<_i21.Cookie> get cookies => (super.noSuchMethod( - Invocation.getter(#cookies), - returnValue: <_i21.Cookie>[], - ) as List<_i21.Cookie>); - @override - bool get isBroadcast => (super.noSuchMethod( - Invocation.getter(#isBroadcast), - returnValue: false, - ) as bool); - @override - _i19.Future get length => (super.noSuchMethod( - Invocation.getter(#length), - returnValue: _i19.Future.value(0), - ) as _i19.Future); - @override - _i19.Future get isEmpty => (super.noSuchMethod( - Invocation.getter(#isEmpty), - returnValue: _i19.Future.value(false), - ) as _i19.Future); - @override - _i19.Future> get first => (super.noSuchMethod( - Invocation.getter(#first), - returnValue: _i19.Future>.value([]), - ) as _i19.Future>); - @override - _i19.Future> get last => (super.noSuchMethod( - Invocation.getter(#last), - returnValue: _i19.Future>.value([]), - ) as _i19.Future>); - @override - _i19.Future> get single => (super.noSuchMethod( - Invocation.getter(#single), - returnValue: _i19.Future>.value([]), - ) as _i19.Future>); - @override - _i19.Future<_i21.HttpClientResponse> redirect([ - String? method, - Uri? url, - bool? followLoops, - ]) => - (super.noSuchMethod( - Invocation.method( - #redirect, - [ - method, - url, - followLoops, - ], - ), - returnValue: _i19.Future<_i21.HttpClientResponse>.value(_FakeHttpClientResponse_48( - this, - Invocation.method( - #redirect, - [ - method, - url, - followLoops, - ], - ), - )), - ) as _i19.Future<_i21.HttpClientResponse>); - @override - _i19.Future<_i21.Socket> detachSocket() => (super.noSuchMethod( - Invocation.method( - #detachSocket, - [], - ), - returnValue: _i19.Future<_i21.Socket>.value(_FakeSocket_50( - this, - Invocation.method( - #detachSocket, - [], - ), - )), - ) as _i19.Future<_i21.Socket>); - @override - _i19.Stream> asBroadcastStream({ - void Function(_i19.StreamSubscription>)? onListen, - void Function(_i19.StreamSubscription>)? onCancel, - }) => - (super.noSuchMethod( - Invocation.method( - #asBroadcastStream, - [], - { - #onListen: onListen, - #onCancel: onCancel, - }, - ), - returnValue: _i19.Stream>.empty(), - ) as _i19.Stream>); - @override - _i19.StreamSubscription> listen( - void Function(List)? onData, { - Function? onError, - void Function()? onDone, - bool? cancelOnError, - }) => - (super.noSuchMethod( - Invocation.method( - #listen, - [onData], - { - #onError: onError, - #onDone: onDone, - #cancelOnError: cancelOnError, - }, - ), - returnValue: _FakeStreamSubscription_51>( - this, - Invocation.method( - #listen, - [onData], - { - #onError: onError, - #onDone: onDone, - #cancelOnError: cancelOnError, - }, - ), - ), - ) as _i19.StreamSubscription>); - @override - _i19.Stream> where(bool Function(List)? test) => (super.noSuchMethod( - Invocation.method( - #where, - [test], - ), - returnValue: _i19.Stream>.empty(), - ) as _i19.Stream>); - @override - _i19.Stream map(S Function(List)? convert) => (super.noSuchMethod( - Invocation.method( - #map, - [convert], - ), - returnValue: _i19.Stream.empty(), - ) as _i19.Stream); - @override - _i19.Stream asyncMap(_i19.FutureOr Function(List)? convert) => (super.noSuchMethod( - Invocation.method( - #asyncMap, - [convert], - ), - returnValue: _i19.Stream.empty(), - ) as _i19.Stream); - @override - _i19.Stream asyncExpand(_i19.Stream? Function(List)? convert) => (super.noSuchMethod( - Invocation.method( - #asyncExpand, - [convert], - ), - returnValue: _i19.Stream.empty(), - ) as _i19.Stream); - @override - _i19.Stream> handleError( - Function? onError, { - bool Function(dynamic)? test, - }) => - (super.noSuchMethod( - Invocation.method( - #handleError, - [onError], - {#test: test}, - ), - returnValue: _i19.Stream>.empty(), - ) as _i19.Stream>); - @override - _i19.Stream expand(Iterable Function(List)? convert) => (super.noSuchMethod( - Invocation.method( - #expand, - [convert], - ), - returnValue: _i19.Stream.empty(), - ) as _i19.Stream); - @override - _i19.Future pipe(_i19.StreamConsumer>? streamConsumer) => (super.noSuchMethod( - Invocation.method( - #pipe, - [streamConsumer], - ), - returnValue: _i19.Future.value(), - ) as _i19.Future); - @override - _i19.Stream transform(_i19.StreamTransformer, S>? streamTransformer) => (super.noSuchMethod( - Invocation.method( - #transform, - [streamTransformer], - ), - returnValue: _i19.Stream.empty(), - ) as _i19.Stream); - @override - _i19.Future> reduce( - List Function( - List, - List, - )? combine) => - (super.noSuchMethod( - Invocation.method( - #reduce, - [combine], - ), - returnValue: _i19.Future>.value([]), - ) as _i19.Future>); - @override - _i19.Future fold( - S? initialValue, - S Function( - S, - List, - )? combine, - ) => - (super.noSuchMethod( - Invocation.method( - #fold, - [ - initialValue, - combine, - ], - ), - returnValue: _i27.ifNotNull( - _i27.dummyValueOrNull( - this, - Invocation.method( - #fold, - [ - initialValue, - combine, - ], - ), - ), - (S v) => _i19.Future.value(v), - ) ?? - _FakeFuture_22( - this, - Invocation.method( - #fold, - [ - initialValue, - combine, - ], - ), - ), - ) as _i19.Future); - @override - _i19.Future join([String? separator = r'']) => (super.noSuchMethod( - Invocation.method( - #join, - [separator], - ), - returnValue: _i19.Future.value(''), - ) as _i19.Future); - @override - _i19.Future contains(Object? needle) => (super.noSuchMethod( - Invocation.method( - #contains, - [needle], - ), - returnValue: _i19.Future.value(false), - ) as _i19.Future); - @override - _i19.Future forEach(void Function(List)? action) => (super.noSuchMethod( - Invocation.method( - #forEach, - [action], - ), - returnValue: _i19.Future.value(), - returnValueForMissingStub: _i19.Future.value(), - ) as _i19.Future); - @override - _i19.Future every(bool Function(List)? test) => (super.noSuchMethod( - Invocation.method( - #every, - [test], - ), - returnValue: _i19.Future.value(false), - ) as _i19.Future); - @override - _i19.Future any(bool Function(List)? test) => (super.noSuchMethod( - Invocation.method( - #any, - [test], - ), - returnValue: _i19.Future.value(false), - ) as _i19.Future); - @override - _i19.Stream cast() => (super.noSuchMethod( - Invocation.method( - #cast, - [], - ), - returnValue: _i19.Stream.empty(), - ) as _i19.Stream); - @override - _i19.Future>> toList() => (super.noSuchMethod( - Invocation.method( - #toList, - [], - ), - returnValue: _i19.Future>>.value(>[]), - ) as _i19.Future>>); - @override - _i19.Future>> toSet() => (super.noSuchMethod( - Invocation.method( - #toSet, - [], - ), - returnValue: _i19.Future>>.value(>{}), - ) as _i19.Future>>); - @override - _i19.Future drain([E? futureValue]) => (super.noSuchMethod( - Invocation.method( - #drain, - [futureValue], - ), - returnValue: _i27.ifNotNull( - _i27.dummyValueOrNull( - this, - Invocation.method( - #drain, - [futureValue], - ), - ), - (E v) => _i19.Future.value(v), - ) ?? - _FakeFuture_22( - this, - Invocation.method( - #drain, - [futureValue], - ), - ), - ) as _i19.Future); - @override - _i19.Stream> take(int? count) => (super.noSuchMethod( - Invocation.method( - #take, - [count], - ), - returnValue: _i19.Stream>.empty(), - ) as _i19.Stream>); - @override - _i19.Stream> takeWhile(bool Function(List)? test) => (super.noSuchMethod( - Invocation.method( - #takeWhile, - [test], - ), - returnValue: _i19.Stream>.empty(), - ) as _i19.Stream>); - @override - _i19.Stream> skip(int? count) => (super.noSuchMethod( - Invocation.method( - #skip, - [count], - ), - returnValue: _i19.Stream>.empty(), - ) as _i19.Stream>); - @override - _i19.Stream> skipWhile(bool Function(List)? test) => (super.noSuchMethod( - Invocation.method( - #skipWhile, - [test], - ), - returnValue: _i19.Stream>.empty(), - ) as _i19.Stream>); - @override - _i19.Stream> distinct( - [bool Function( - List, - List, - )? equals]) => - (super.noSuchMethod( - Invocation.method( - #distinct, - [equals], - ), - returnValue: _i19.Stream>.empty(), - ) as _i19.Stream>); - @override - _i19.Future> firstWhere( - bool Function(List)? test, { - List Function()? orElse, - }) => - (super.noSuchMethod( - Invocation.method( - #firstWhere, - [test], - {#orElse: orElse}, - ), - returnValue: _i19.Future>.value([]), - ) as _i19.Future>); - @override - _i19.Future> lastWhere( - bool Function(List)? test, { - List Function()? orElse, - }) => - (super.noSuchMethod( - Invocation.method( - #lastWhere, - [test], - {#orElse: orElse}, - ), - returnValue: _i19.Future>.value([]), - ) as _i19.Future>); - @override - _i19.Future> singleWhere( - bool Function(List)? test, { - List Function()? orElse, - }) => - (super.noSuchMethod( - Invocation.method( - #singleWhere, - [test], - {#orElse: orElse}, - ), - returnValue: _i19.Future>.value([]), - ) as _i19.Future>); - @override - _i19.Future> elementAt(int? index) => (super.noSuchMethod( - Invocation.method( - #elementAt, - [index], - ), - returnValue: _i19.Future>.value([]), - ) as _i19.Future>); - @override - _i19.Stream> timeout( - Duration? timeLimit, { - void Function(_i19.EventSink>)? onTimeout, - }) => - (super.noSuchMethod( - Invocation.method( - #timeout, - [timeLimit], - {#onTimeout: onTimeout}, - ), - returnValue: _i19.Stream>.empty(), - ) as _i19.Stream>); -} - -/// A class which mocks [JobsResource]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockJobsResource extends _i1.Mock implements _i6.JobsResource { - MockJobsResource() { - _i1.throwOnMissingStub(this); - } - - @override - _i19.Future<_i6.JobCancelResponse> cancel( - String? projectId, - String? jobId, { - String? location, - String? $fields, - }) => - (super.noSuchMethod( - Invocation.method( - #cancel, - [ - projectId, - jobId, - ], - { - #location: location, - #$fields: $fields, - }, - ), - returnValue: _i19.Future<_i6.JobCancelResponse>.value(_FakeJobCancelResponse_52( - this, - Invocation.method( - #cancel, - [ - projectId, - jobId, - ], - { - #location: location, - #$fields: $fields, - }, - ), - )), - ) as _i19.Future<_i6.JobCancelResponse>); - @override - _i19.Future delete( - String? projectId, - String? jobId, { - String? location, - String? $fields, - }) => - (super.noSuchMethod( - Invocation.method( - #delete, - [ - projectId, - jobId, - ], - { - #location: location, - #$fields: $fields, - }, - ), - returnValue: _i19.Future.value(), - returnValueForMissingStub: _i19.Future.value(), - ) as _i19.Future); - @override - _i19.Future<_i6.Job> get( - String? projectId, - String? jobId, { - String? location, - String? $fields, - }) => - (super.noSuchMethod( - Invocation.method( - #get, - [ - projectId, - jobId, - ], - { - #location: location, - #$fields: $fields, - }, - ), - returnValue: _i19.Future<_i6.Job>.value(_FakeJob_53( - this, - Invocation.method( - #get, - [ - projectId, - jobId, - ], - { - #location: location, - #$fields: $fields, - }, - ), - )), - ) as _i19.Future<_i6.Job>); - @override - _i19.Future<_i6.GetQueryResultsResponse> getQueryResults( - String? projectId, - String? jobId, { - String? location, - int? maxResults, - String? pageToken, - String? startIndex, - int? timeoutMs, - String? $fields, - }) => - (super.noSuchMethod( - Invocation.method( - #getQueryResults, - [ - projectId, - jobId, - ], - { - #location: location, - #maxResults: maxResults, - #pageToken: pageToken, - #startIndex: startIndex, - #timeoutMs: timeoutMs, - #$fields: $fields, - }, - ), - returnValue: _i19.Future<_i6.GetQueryResultsResponse>.value(_FakeGetQueryResultsResponse_54( - this, - Invocation.method( - #getQueryResults, - [ - projectId, - jobId, - ], - { - #location: location, - #maxResults: maxResults, - #pageToken: pageToken, - #startIndex: startIndex, - #timeoutMs: timeoutMs, - #$fields: $fields, - }, - ), - )), - ) as _i19.Future<_i6.GetQueryResultsResponse>); - @override - _i19.Future<_i6.Job> insert( - _i6.Job? request, - String? projectId, { - String? $fields, - _i6.UploadOptions? uploadOptions = _i6.UploadOptions.defaultOptions, - _i6.Media? uploadMedia, - }) => - (super.noSuchMethod( - Invocation.method( - #insert, - [ - request, - projectId, - ], - { - #$fields: $fields, - #uploadOptions: uploadOptions, - #uploadMedia: uploadMedia, - }, - ), - returnValue: _i19.Future<_i6.Job>.value(_FakeJob_53( - this, - Invocation.method( - #insert, - [ - request, - projectId, - ], - { - #$fields: $fields, - #uploadOptions: uploadOptions, - #uploadMedia: uploadMedia, - }, - ), - )), - ) as _i19.Future<_i6.Job>); - @override - _i19.Future<_i6.JobList> list( - String? projectId, { - bool? allUsers, - String? maxCreationTime, - int? maxResults, - String? minCreationTime, - String? pageToken, - String? parentJobId, - String? projection, - List? stateFilter, - String? $fields, - }) => - (super.noSuchMethod( - Invocation.method( - #list, - [projectId], - { - #allUsers: allUsers, - #maxCreationTime: maxCreationTime, - #maxResults: maxResults, - #minCreationTime: minCreationTime, - #pageToken: pageToken, - #parentJobId: parentJobId, - #projection: projection, - #stateFilter: stateFilter, - #$fields: $fields, - }, - ), - returnValue: _i19.Future<_i6.JobList>.value(_FakeJobList_55( - this, - Invocation.method( - #list, - [projectId], - { - #allUsers: allUsers, - #maxCreationTime: maxCreationTime, - #maxResults: maxResults, - #minCreationTime: minCreationTime, - #pageToken: pageToken, - #parentJobId: parentJobId, - #projection: projection, - #stateFilter: stateFilter, - #$fields: $fields, - }, - ), - )), - ) as _i19.Future<_i6.JobList>); - @override - _i19.Future<_i6.QueryResponse> query( - _i6.QueryRequest? request, - String? projectId, { - String? $fields, - }) => - (super.noSuchMethod( - Invocation.method( - #query, - [ - request, - projectId, - ], - {#$fields: $fields}, - ), - returnValue: _i19.Future<_i6.QueryResponse>.value(_FakeQueryResponse_56( - this, - Invocation.method( - #query, - [ - request, - projectId, - ], - {#$fields: $fields}, - ), - )), - ) as _i19.Future<_i6.QueryResponse>); -} - -/// A class which mocks [LuciBuildService]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockLuciBuildService extends _i1.Mock implements _i23.LuciBuildService { - MockLuciBuildService() { - _i1.throwOnMissingStub(this); - } - - @override - _i23.BuildBucketClient get buildBucketClient => (super.noSuchMethod( - Invocation.getter(#buildBucketClient), - returnValue: _FakeBuildBucketClient_57( - this, - Invocation.getter(#buildBucketClient), - ), - ) as _i23.BuildBucketClient); - @override - set buildBucketClient(_i23.BuildBucketClient? _buildBucketClient) => super.noSuchMethod( - Invocation.setter( - #buildBucketClient, - _buildBucketClient, - ), - returnValueForMissingStub: null, - ); - @override - _i23.CacheService get cache => (super.noSuchMethod( - Invocation.getter(#cache), - returnValue: _FakeCacheService_58( - this, - Invocation.getter(#cache), - ), - ) as _i23.CacheService); - @override - _i3.Config get config => (super.noSuchMethod( - Invocation.getter(#config), - returnValue: _FakeConfig_1( - this, - Invocation.getter(#config), - ), - ) as _i3.Config); - @override - set config(_i3.Config? _config) => super.noSuchMethod( - Invocation.setter( - #config, - _config, - ), - returnValueForMissingStub: null, - ); - @override - _i20.GithubChecksUtil get githubChecksUtil => (super.noSuchMethod( - Invocation.getter(#githubChecksUtil), - returnValue: _FakeGithubChecksUtil_27( - this, - Invocation.getter(#githubChecksUtil), - ), - ) as _i20.GithubChecksUtil); - @override - set githubChecksUtil(_i20.GithubChecksUtil? _githubChecksUtil) => super.noSuchMethod( - Invocation.setter( - #githubChecksUtil, - _githubChecksUtil, - ), - returnValueForMissingStub: null, - ); - @override - _i7.GerritService get gerritService => (super.noSuchMethod( - Invocation.getter(#gerritService), - returnValue: _FakeGerritService_6( - this, - Invocation.getter(#gerritService), - ), - ) as _i7.GerritService); - @override - set gerritService(_i7.GerritService? _gerritService) => super.noSuchMethod( - Invocation.setter( - #gerritService, - _gerritService, - ), - returnValueForMissingStub: null, - ); - @override - _i23.PubSub get pubsub => (super.noSuchMethod( - Invocation.getter(#pubsub), - returnValue: _FakePubSub_59( - this, - Invocation.getter(#pubsub), - ), - ) as _i23.PubSub); - @override - _i19.Future>> shard( - List<_i8.Request>? requests, - int? max, - ) => - (super.noSuchMethod( - Invocation.method( - #shard, - [ - requests, - max, - ], - ), - returnValue: _i19.Future>>.value(>[]), - ) as _i19.Future>>); - @override - _i19.Future> getTryBuilds( - _i13.RepositorySlug? slug, - String? sha, - String? builderName, - ) => - (super.noSuchMethod( - Invocation.method( - #getTryBuilds, - [ - slug, - sha, - builderName, - ], - ), - returnValue: _i19.Future>.value(<_i8.Build>[]), - ) as _i19.Future>); - @override - _i19.Future> getTryBuildsByPullRequest(_i13.PullRequest? pullRequest) => (super.noSuchMethod( - Invocation.method( - #getTryBuildsByPullRequest, - [pullRequest], - ), - returnValue: _i19.Future>.value(<_i8.Build>[]), - ) as _i19.Future>); - @override - _i19.Future> getProdBuilds( - _i13.RepositorySlug? slug, - String? commitSha, - String? builderName, - ) => - (super.noSuchMethod( - Invocation.method( - #getProdBuilds, - [ - slug, - commitSha, - builderName, - ], - ), - returnValue: _i19.Future>.value(<_i8.Build>[]), - ) as _i19.Future>); - @override - _i19.Future> getBuilds( - _i13.RepositorySlug? slug, - String? commitSha, - String? builderName, - String? bucket, - Map>? tags, - ) => - (super.noSuchMethod( - Invocation.method( - #getBuilds, - [ - slug, - commitSha, - builderName, - bucket, - tags, - ], - ), - returnValue: _i19.Future>.value(<_i8.Build>[]), - ) as _i19.Future>); - @override - _i19.Future> scheduleTryBuilds({ - required List<_i37.Target>? targets, - required _i13.PullRequest? pullRequest, - _i29.CheckSuiteEvent? checkSuiteEvent, - }) => - (super.noSuchMethod( - Invocation.method( - #scheduleTryBuilds, - [], - { - #targets: targets, - #pullRequest: pullRequest, - #checkSuiteEvent: checkSuiteEvent, - }, - ), - returnValue: _i19.Future>.value(<_i37.Target>[]), - ) as _i19.Future>); - @override - _i19.Future cancelBuilds( - _i13.PullRequest? pullRequest, - String? reason, - ) => - (super.noSuchMethod( - Invocation.method( - #cancelBuilds, - [ - pullRequest, - reason, - ], - ), - returnValue: _i19.Future.value(), - returnValueForMissingStub: _i19.Future.value(), - ) as _i19.Future); - @override - _i19.Future> failedBuilds( - _i13.PullRequest? pullRequest, - List<_i37.Target>? targets, - ) => - (super.noSuchMethod( - Invocation.method( - #failedBuilds, - [ - pullRequest, - targets, - ], - ), - returnValue: _i19.Future>.value(<_i8.Build?>[]), - ) as _i19.Future>); - @override - _i19.Future<_i8.Build> rescheduleBuild({ - required String? builderName, - required _i36.BuildPushMessage? buildPushMessage, - required int? rescheduleAttempt, - }) => - (super.noSuchMethod( - Invocation.method( - #rescheduleBuild, - [], - { - #builderName: builderName, - #buildPushMessage: buildPushMessage, - #rescheduleAttempt: rescheduleAttempt, - }, - ), - returnValue: _i19.Future<_i8.Build>.value(_FakeBuild_7( - this, - Invocation.method( - #rescheduleBuild, - [], - { - #builderName: builderName, - #buildPushMessage: buildPushMessage, - #rescheduleAttempt: rescheduleAttempt, - }, - ), - )), - ) as _i19.Future<_i8.Build>); - @override - _i19.Future<_i8.Build> reschedulePresubmitBuildUsingCheckRunEvent(_i38.CheckRunEvent? checkRunEvent) => - (super.noSuchMethod( - Invocation.method( - #reschedulePresubmitBuildUsingCheckRunEvent, - [checkRunEvent], - ), - returnValue: _i19.Future<_i8.Build>.value(_FakeBuild_7( - this, - Invocation.method( - #reschedulePresubmitBuildUsingCheckRunEvent, - [checkRunEvent], - ), - )), - ) as _i19.Future<_i8.Build>); - @override - _i19.Future<_i8.Build> reschedulePostsubmitBuildUsingCheckRunEvent( - _i38.CheckRunEvent? checkRunEvent, { - required _i31.Commit? commit, - required _i32.Task? task, - required _i37.Target? target, - }) => - (super.noSuchMethod( - Invocation.method( - #reschedulePostsubmitBuildUsingCheckRunEvent, - [checkRunEvent], - { - #commit: commit, - #task: task, - #target: target, - }, - ), - returnValue: _i19.Future<_i8.Build>.value(_FakeBuild_7( - this, - Invocation.method( - #reschedulePostsubmitBuildUsingCheckRunEvent, - [checkRunEvent], - { - #commit: commit, - #task: task, - #target: target, - }, - ), - )), - ) as _i19.Future<_i8.Build>); - @override - _i19.Future<_i8.Build> getBuildById( - String? id, { - String? fields, - }) => - (super.noSuchMethod( - Invocation.method( - #getBuildById, - [id], - {#fields: fields}, - ), - returnValue: _i19.Future<_i8.Build>.value(_FakeBuild_7( - this, - Invocation.method( - #getBuildById, - [id], - {#fields: fields}, - ), - )), - ) as _i19.Future<_i8.Build>); - @override - _i19.Future> getAvailableBuilderSet({ - String? project = r'flutter', - String? bucket = r'prod', - }) => - (super.noSuchMethod( - Invocation.method( - #getAvailableBuilderSet, - [], - { - #project: project, - #bucket: bucket, - }, - ), - returnValue: _i19.Future>.value({}), - ) as _i19.Future>); - @override - _i19.Future>> schedulePostsubmitBuilds({ - required _i31.Commit? commit, - required List<_i23.Tuple<_i37.Target, _i32.Task, int>>? toBeScheduled, - }) => - (super.noSuchMethod( - Invocation.method( - #schedulePostsubmitBuilds, - [], - { - #commit: commit, - #toBeScheduled: toBeScheduled, - }, - ), - returnValue: _i19.Future>>.value( - <_i23.Tuple<_i37.Target, _i32.Task, int>>[]), - ) as _i19.Future>>); - @override - _i19.Future createPostsubmitCheckRun( - _i31.Commit? commit, - _i37.Target? target, - Map? rawUserData, - ) => - (super.noSuchMethod( - Invocation.method( - #createPostsubmitCheckRun, - [ - commit, - target, - rawUserData, - ], - ), - returnValue: _i19.Future.value(), - returnValueForMissingStub: _i19.Future.value(), - ) as _i19.Future); - @override - _i19.Future checkRerunBuilder({ - required _i31.Commit? commit, - required _i37.Target? target, - required _i32.Task? task, - required _i9.DatastoreService? datastore, - Map>? tags, - bool? ignoreChecks = false, - }) => - (super.noSuchMethod( - Invocation.method( - #checkRerunBuilder, - [], - { - #commit: commit, - #target: target, - #task: task, - #datastore: datastore, - #tags: tags, - #ignoreChecks: ignoreChecks, - }, - ), - returnValue: _i19.Future.value(false), - ) as _i19.Future); -} - -/// A class which mocks [ProcessManager]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockProcessManager extends _i1.Mock implements _i39.ProcessManager { - MockProcessManager() { - _i1.throwOnMissingStub(this); - } - - @override - _i19.Future<_i21.Process> start( - List? command, { - String? workingDirectory, - Map? environment, - bool? includeParentEnvironment = true, - bool? runInShell = false, - _i21.ProcessStartMode? mode = _i21.ProcessStartMode.normal, - }) => - (super.noSuchMethod( - Invocation.method( - #start, - [command], - { - #workingDirectory: workingDirectory, - #environment: environment, - #includeParentEnvironment: includeParentEnvironment, - #runInShell: runInShell, - #mode: mode, - }, - ), - returnValue: _i19.Future<_i21.Process>.value(_FakeProcess_60( - this, - Invocation.method( - #start, - [command], - { - #workingDirectory: workingDirectory, - #environment: environment, - #includeParentEnvironment: includeParentEnvironment, - #runInShell: runInShell, - #mode: mode, - }, - ), - )), - ) as _i19.Future<_i21.Process>); - @override - _i19.Future<_i21.ProcessResult> run( - List? command, { - String? workingDirectory, - Map? environment, - bool? includeParentEnvironment = true, - bool? runInShell = false, - _i22.Encoding? stdoutEncoding = const _i21.SystemEncoding(), - _i22.Encoding? stderrEncoding = const _i21.SystemEncoding(), - }) => - (super.noSuchMethod( - Invocation.method( - #run, - [command], - { - #workingDirectory: workingDirectory, - #environment: environment, - #includeParentEnvironment: includeParentEnvironment, - #runInShell: runInShell, - #stdoutEncoding: stdoutEncoding, - #stderrEncoding: stderrEncoding, - }, - ), - returnValue: _i19.Future<_i21.ProcessResult>.value(_i27.dummyValue<_i21.ProcessResult>( - this, - Invocation.method( - #run, - [command], - { - #workingDirectory: workingDirectory, - #environment: environment, - #includeParentEnvironment: includeParentEnvironment, - #runInShell: runInShell, - #stdoutEncoding: stdoutEncoding, - #stderrEncoding: stderrEncoding, - }, - ), - )), - ) as _i19.Future<_i21.ProcessResult>); - @override - _i21.ProcessResult runSync( - List? command, { - String? workingDirectory, - Map? environment, - bool? includeParentEnvironment = true, - bool? runInShell = false, - _i22.Encoding? stdoutEncoding = const _i21.SystemEncoding(), - _i22.Encoding? stderrEncoding = const _i21.SystemEncoding(), - }) => - (super.noSuchMethod( - Invocation.method( - #runSync, - [command], - { - #workingDirectory: workingDirectory, - #environment: environment, - #includeParentEnvironment: includeParentEnvironment, - #runInShell: runInShell, - #stdoutEncoding: stdoutEncoding, - #stderrEncoding: stderrEncoding, - }, - ), - returnValue: _i27.dummyValue<_i21.ProcessResult>( - this, - Invocation.method( - #runSync, - [command], - { - #workingDirectory: workingDirectory, - #environment: environment, - #includeParentEnvironment: includeParentEnvironment, - #runInShell: runInShell, - #stdoutEncoding: stdoutEncoding, - #stderrEncoding: stderrEncoding, - }, - ), - ), - ) as _i21.ProcessResult); - @override - bool canRun( - dynamic executable, { - String? workingDirectory, - }) => - (super.noSuchMethod( - Invocation.method( - #canRun, - [executable], - {#workingDirectory: workingDirectory}, - ), - returnValue: false, - ) as bool); - @override - bool killPid( - int? pid, [ - _i21.ProcessSignal? signal = _i21.ProcessSignal.sigterm, - ]) => - (super.noSuchMethod( - Invocation.method( - #killPid, - [ - pid, - signal, - ], - ), - returnValue: false, - ) as bool); -} - -/// A class which mocks [PullRequestsService]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockPullRequestsService extends _i1.Mock implements _i13.PullRequestsService { - MockPullRequestsService() { - _i1.throwOnMissingStub(this); - } - - @override - _i13.GitHub get github => (super.noSuchMethod( - Invocation.getter(#github), - returnValue: _FakeGitHub_16( - this, - Invocation.getter(#github), - ), - ) as _i13.GitHub); - @override - _i19.Stream<_i13.PullRequest> list( - _i13.RepositorySlug? slug, { - int? pages, - String? base, - String? direction = r'desc', - String? head, - String? sort = r'created', - String? state = r'open', - }) => - (super.noSuchMethod( - Invocation.method( - #list, - [slug], - { - #pages: pages, - #base: base, - #direction: direction, - #head: head, - #sort: sort, - #state: state, - }, - ), - returnValue: _i19.Stream<_i13.PullRequest>.empty(), - ) as _i19.Stream<_i13.PullRequest>); - @override - _i19.Future<_i13.PullRequest> get( - _i13.RepositorySlug? slug, - int? number, - ) => - (super.noSuchMethod( - Invocation.method( - #get, - [ - slug, - number, - ], - ), - returnValue: _i19.Future<_i13.PullRequest>.value(_FakePullRequest_32( - this, - Invocation.method( - #get, - [ - slug, - number, - ], - ), - )), - ) as _i19.Future<_i13.PullRequest>); - @override - _i19.Future<_i13.PullRequest> create( - _i13.RepositorySlug? slug, - _i13.CreatePullRequest? request, - ) => - (super.noSuchMethod( - Invocation.method( - #create, - [ - slug, - request, - ], - ), - returnValue: _i19.Future<_i13.PullRequest>.value(_FakePullRequest_32( - this, - Invocation.method( - #create, - [ - slug, - request, - ], - ), - )), - ) as _i19.Future<_i13.PullRequest>); - @override - _i19.Future<_i13.PullRequest> edit( - _i13.RepositorySlug? slug, - int? number, { - String? title, - String? body, - String? state, - String? base, - }) => - (super.noSuchMethod( - Invocation.method( - #edit, - [ - slug, - number, - ], - { - #title: title, - #body: body, - #state: state, - #base: base, - }, - ), - returnValue: _i19.Future<_i13.PullRequest>.value(_FakePullRequest_32( - this, - Invocation.method( - #edit, - [ - slug, - number, - ], - { - #title: title, - #body: body, - #state: state, - #base: base, - }, - ), - )), - ) as _i19.Future<_i13.PullRequest>); - @override - _i19.Stream<_i13.RepositoryCommit> listCommits( - _i13.RepositorySlug? slug, - int? number, - ) => - (super.noSuchMethod( - Invocation.method( - #listCommits, - [ - slug, - number, - ], - ), - returnValue: _i19.Stream<_i13.RepositoryCommit>.empty(), - ) as _i19.Stream<_i13.RepositoryCommit>); - @override - _i19.Stream<_i13.PullRequestFile> listFiles( - _i13.RepositorySlug? slug, - int? number, - ) => - (super.noSuchMethod( - Invocation.method( - #listFiles, - [ - slug, - number, - ], - ), - returnValue: _i19.Stream<_i13.PullRequestFile>.empty(), - ) as _i19.Stream<_i13.PullRequestFile>); - @override - _i19.Stream<_i13.PullRequestReview> listReviews( - _i13.RepositorySlug? slug, - int? number, - ) => - (super.noSuchMethod( - Invocation.method( - #listReviews, - [ - slug, - number, - ], - ), - returnValue: _i19.Stream<_i13.PullRequestReview>.empty(), - ) as _i19.Stream<_i13.PullRequestReview>); - @override - _i19.Future isMerged( - _i13.RepositorySlug? slug, - int? number, - ) => - (super.noSuchMethod( - Invocation.method( - #isMerged, - [ - slug, - number, - ], - ), - returnValue: _i19.Future.value(false), - ) as _i19.Future); - @override - _i19.Future<_i13.PullRequestMerge> merge( - _i13.RepositorySlug? slug, - int? number, { - String? message, - _i13.MergeMethod? mergeMethod = _i13.MergeMethod.merge, - String? requestSha, - }) => - (super.noSuchMethod( - Invocation.method( - #merge, - [ - slug, - number, - ], - { - #message: message, - #mergeMethod: mergeMethod, - #requestSha: requestSha, - }, - ), - returnValue: _i19.Future<_i13.PullRequestMerge>.value(_FakePullRequestMerge_61( - this, - Invocation.method( - #merge, - [ - slug, - number, - ], - { - #message: message, - #mergeMethod: mergeMethod, - #requestSha: requestSha, - }, - ), - )), - ) as _i19.Future<_i13.PullRequestMerge>); - @override - _i19.Stream<_i13.PullRequestComment> listCommentsByPullRequest( - _i13.RepositorySlug? slug, - int? number, - ) => - (super.noSuchMethod( - Invocation.method( - #listCommentsByPullRequest, - [ - slug, - number, - ], - ), - returnValue: _i19.Stream<_i13.PullRequestComment>.empty(), - ) as _i19.Stream<_i13.PullRequestComment>); - @override - _i19.Stream<_i13.PullRequestComment> listComments(_i13.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listComments, - [slug], - ), - returnValue: _i19.Stream<_i13.PullRequestComment>.empty(), - ) as _i19.Stream<_i13.PullRequestComment>); - @override - _i19.Future<_i13.PullRequestComment> createComment( - _i13.RepositorySlug? slug, - int? number, - _i13.CreatePullRequestComment? comment, - ) => - (super.noSuchMethod( - Invocation.method( - #createComment, - [ - slug, - number, - comment, - ], - ), - returnValue: _i19.Future<_i13.PullRequestComment>.value(_FakePullRequestComment_62( - this, - Invocation.method( - #createComment, - [ - slug, - number, - comment, - ], - ), - )), - ) as _i19.Future<_i13.PullRequestComment>); - @override - _i19.Future<_i13.PullRequestReview> createReview( - _i13.RepositorySlug? slug, - _i13.CreatePullRequestReview? review, - ) => - (super.noSuchMethod( - Invocation.method( - #createReview, - [ - slug, - review, - ], - ), - returnValue: _i19.Future<_i13.PullRequestReview>.value(_FakePullRequestReview_63( - this, - Invocation.method( - #createReview, - [ - slug, - review, - ], - ), - )), - ) as _i19.Future<_i13.PullRequestReview>); -} - -/// A class which mocks [RepositoriesService]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockRepositoriesService extends _i1.Mock implements _i13.RepositoriesService { - MockRepositoriesService() { - _i1.throwOnMissingStub(this); - } - - @override - _i13.GitHub get github => (super.noSuchMethod( - Invocation.getter(#github), - returnValue: _FakeGitHub_16( - this, - Invocation.getter(#github), - ), - ) as _i13.GitHub); - @override - _i19.Stream<_i13.Repository> listRepositories({ - String? type = r'owner', - String? sort = r'full_name', - String? direction = r'asc', - }) => - (super.noSuchMethod( - Invocation.method( - #listRepositories, - [], - { - #type: type, - #sort: sort, - #direction: direction, - }, - ), - returnValue: _i19.Stream<_i13.Repository>.empty(), - ) as _i19.Stream<_i13.Repository>); - @override - _i19.Stream<_i13.Repository> listUserRepositories( - String? user, { - String? type = r'owner', - String? sort = r'full_name', - String? direction = r'asc', - }) => - (super.noSuchMethod( - Invocation.method( - #listUserRepositories, - [user], - { - #type: type, - #sort: sort, - #direction: direction, - }, - ), - returnValue: _i19.Stream<_i13.Repository>.empty(), - ) as _i19.Stream<_i13.Repository>); - @override - _i19.Stream<_i13.Repository> listOrganizationRepositories( - String? org, { - String? type = r'all', - }) => - (super.noSuchMethod( - Invocation.method( - #listOrganizationRepositories, - [org], - {#type: type}, - ), - returnValue: _i19.Stream<_i13.Repository>.empty(), - ) as _i19.Stream<_i13.Repository>); - @override - _i19.Stream<_i13.Repository> listPublicRepositories({ - int? limit = 50, - DateTime? since, - }) => - (super.noSuchMethod( - Invocation.method( - #listPublicRepositories, - [], - { - #limit: limit, - #since: since, - }, - ), - returnValue: _i19.Stream<_i13.Repository>.empty(), - ) as _i19.Stream<_i13.Repository>); - @override - _i19.Future<_i13.Repository> createRepository( - _i13.CreateRepository? repository, { - String? org, - }) => - (super.noSuchMethod( - Invocation.method( - #createRepository, - [repository], - {#org: org}, - ), - returnValue: _i19.Future<_i13.Repository>.value(_FakeRepository_64( - this, - Invocation.method( - #createRepository, - [repository], - {#org: org}, - ), - )), - ) as _i19.Future<_i13.Repository>); - @override - _i19.Future<_i13.LicenseDetails> getLicense(_i13.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #getLicense, - [slug], - ), - returnValue: _i19.Future<_i13.LicenseDetails>.value(_FakeLicenseDetails_65( - this, - Invocation.method( - #getLicense, - [slug], - ), - )), - ) as _i19.Future<_i13.LicenseDetails>); - @override - _i19.Future<_i13.Repository> getRepository(_i13.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #getRepository, - [slug], - ), - returnValue: _i19.Future<_i13.Repository>.value(_FakeRepository_64( - this, - Invocation.method( - #getRepository, - [slug], - ), - )), - ) as _i19.Future<_i13.Repository>); - @override - _i19.Stream<_i13.Repository> getRepositories(List<_i13.RepositorySlug>? slugs) => (super.noSuchMethod( - Invocation.method( - #getRepositories, - [slugs], - ), - returnValue: _i19.Stream<_i13.Repository>.empty(), - ) as _i19.Stream<_i13.Repository>); - @override - _i19.Future<_i13.Repository> editRepository( - _i13.RepositorySlug? slug, { - String? name, - String? description, - String? homepage, - bool? private, - bool? hasIssues, - bool? hasWiki, - bool? hasDownloads, - }) => - (super.noSuchMethod( - Invocation.method( - #editRepository, - [slug], - { - #name: name, - #description: description, - #homepage: homepage, - #private: private, - #hasIssues: hasIssues, - #hasWiki: hasWiki, - #hasDownloads: hasDownloads, - }, - ), - returnValue: _i19.Future<_i13.Repository>.value(_FakeRepository_64( - this, - Invocation.method( - #editRepository, - [slug], - { - #name: name, - #description: description, - #homepage: homepage, - #private: private, - #hasIssues: hasIssues, - #hasWiki: hasWiki, - #hasDownloads: hasDownloads, - }, - ), - )), - ) as _i19.Future<_i13.Repository>); - @override - _i19.Future deleteRepository(_i13.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #deleteRepository, - [slug], - ), - returnValue: _i19.Future.value(false), - ) as _i19.Future); - @override - _i19.Stream<_i13.Contributor> listContributors( - _i13.RepositorySlug? slug, { - bool? anon = false, - }) => - (super.noSuchMethod( - Invocation.method( - #listContributors, - [slug], - {#anon: anon}, - ), - returnValue: _i19.Stream<_i13.Contributor>.empty(), - ) as _i19.Stream<_i13.Contributor>); - @override - _i19.Stream<_i13.Team> listTeams(_i13.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listTeams, - [slug], - ), - returnValue: _i19.Stream<_i13.Team>.empty(), - ) as _i19.Stream<_i13.Team>); - @override - _i19.Future<_i13.LanguageBreakdown> listLanguages(_i13.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listLanguages, - [slug], - ), - returnValue: _i19.Future<_i13.LanguageBreakdown>.value(_FakeLanguageBreakdown_66( - this, - Invocation.method( - #listLanguages, - [slug], - ), - )), - ) as _i19.Future<_i13.LanguageBreakdown>); - @override - _i19.Stream<_i13.Tag> listTags( - _i13.RepositorySlug? slug, { - int? page = 1, - int? pages, - int? perPage = 30, - }) => - (super.noSuchMethod( - Invocation.method( - #listTags, - [slug], - { - #page: page, - #pages: pages, - #perPage: perPage, - }, - ), - returnValue: _i19.Stream<_i13.Tag>.empty(), - ) as _i19.Stream<_i13.Tag>); - @override - _i19.Stream<_i13.Branch> listBranches(_i13.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listBranches, - [slug], - ), - returnValue: _i19.Stream<_i13.Branch>.empty(), - ) as _i19.Stream<_i13.Branch>); - @override - _i19.Future<_i13.Branch> getBranch( - _i13.RepositorySlug? slug, - String? branch, - ) => - (super.noSuchMethod( - Invocation.method( - #getBranch, - [ - slug, - branch, - ], - ), - returnValue: _i19.Future<_i13.Branch>.value(_FakeBranch_67( - this, - Invocation.method( - #getBranch, - [ - slug, - branch, - ], - ), - )), - ) as _i19.Future<_i13.Branch>); - @override - _i19.Stream<_i13.Collaborator> listCollaborators(_i13.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listCollaborators, - [slug], - ), - returnValue: _i19.Stream<_i13.Collaborator>.empty(), - ) as _i19.Stream<_i13.Collaborator>); - @override - _i19.Future isCollaborator( - _i13.RepositorySlug? slug, - String? user, - ) => - (super.noSuchMethod( - Invocation.method( - #isCollaborator, - [ - slug, - user, - ], - ), - returnValue: _i19.Future.value(false), - ) as _i19.Future); - @override - _i19.Future addCollaborator( - _i13.RepositorySlug? slug, - String? user, - ) => - (super.noSuchMethod( - Invocation.method( - #addCollaborator, - [ - slug, - user, - ], - ), - returnValue: _i19.Future.value(false), - ) as _i19.Future); - @override - _i19.Future removeCollaborator( - _i13.RepositorySlug? slug, - String? user, - ) => - (super.noSuchMethod( - Invocation.method( - #removeCollaborator, - [ - slug, - user, - ], - ), - returnValue: _i19.Future.value(false), - ) as _i19.Future); - @override - _i19.Stream<_i13.CommitComment> listSingleCommitComments( - _i13.RepositorySlug? slug, - _i13.RepositoryCommit? commit, - ) => - (super.noSuchMethod( - Invocation.method( - #listSingleCommitComments, - [ - slug, - commit, - ], - ), - returnValue: _i19.Stream<_i13.CommitComment>.empty(), - ) as _i19.Stream<_i13.CommitComment>); - @override - _i19.Stream<_i13.CommitComment> listCommitComments(_i13.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listCommitComments, - [slug], - ), - returnValue: _i19.Stream<_i13.CommitComment>.empty(), - ) as _i19.Stream<_i13.CommitComment>); - @override - _i19.Future<_i13.CommitComment> createCommitComment( - _i13.RepositorySlug? slug, - _i13.RepositoryCommit? commit, { - required String? body, - String? path, - int? position, - int? line, - }) => - (super.noSuchMethod( - Invocation.method( - #createCommitComment, - [ - slug, - commit, - ], - { - #body: body, - #path: path, - #position: position, - #line: line, - }, - ), - returnValue: _i19.Future<_i13.CommitComment>.value(_FakeCommitComment_68( - this, - Invocation.method( - #createCommitComment, - [ - slug, - commit, - ], - { - #body: body, - #path: path, - #position: position, - #line: line, - }, - ), - )), - ) as _i19.Future<_i13.CommitComment>); - @override - _i19.Future<_i13.CommitComment> getCommitComment( - _i13.RepositorySlug? slug, { - required int? id, - }) => - (super.noSuchMethod( - Invocation.method( - #getCommitComment, - [slug], - {#id: id}, - ), - returnValue: _i19.Future<_i13.CommitComment>.value(_FakeCommitComment_68( - this, - Invocation.method( - #getCommitComment, - [slug], - {#id: id}, - ), - )), - ) as _i19.Future<_i13.CommitComment>); - @override - _i19.Future<_i13.CommitComment> updateCommitComment( - _i13.RepositorySlug? slug, { - required int? id, - required String? body, - }) => - (super.noSuchMethod( - Invocation.method( - #updateCommitComment, - [slug], - { - #id: id, - #body: body, - }, - ), - returnValue: _i19.Future<_i13.CommitComment>.value(_FakeCommitComment_68( - this, - Invocation.method( - #updateCommitComment, - [slug], - { - #id: id, - #body: body, - }, - ), - )), - ) as _i19.Future<_i13.CommitComment>); - @override - _i19.Future deleteCommitComment( - _i13.RepositorySlug? slug, { - required int? id, - }) => - (super.noSuchMethod( - Invocation.method( - #deleteCommitComment, - [slug], - {#id: id}, - ), - returnValue: _i19.Future.value(false), - ) as _i19.Future); - @override - _i19.Stream<_i13.RepositoryCommit> listCommits( - _i13.RepositorySlug? slug, { - String? sha, - String? path, - String? author, - String? committer, - DateTime? since, - DateTime? until, - }) => - (super.noSuchMethod( - Invocation.method( - #listCommits, - [slug], - { - #sha: sha, - #path: path, - #author: author, - #committer: committer, - #since: since, - #until: until, - }, - ), - returnValue: _i19.Stream<_i13.RepositoryCommit>.empty(), - ) as _i19.Stream<_i13.RepositoryCommit>); - @override - _i19.Future<_i13.RepositoryCommit> getCommit( - _i13.RepositorySlug? slug, - String? sha, - ) => - (super.noSuchMethod( - Invocation.method( - #getCommit, - [ - slug, - sha, - ], - ), - returnValue: _i19.Future<_i13.RepositoryCommit>.value(_FakeRepositoryCommit_69( - this, - Invocation.method( - #getCommit, - [ - slug, - sha, - ], - ), - )), - ) as _i19.Future<_i13.RepositoryCommit>); - @override - _i19.Future getCommitDiff( - _i13.RepositorySlug? slug, - String? sha, - ) => - (super.noSuchMethod( - Invocation.method( - #getCommitDiff, - [ - slug, - sha, - ], - ), - returnValue: _i19.Future.value(''), - ) as _i19.Future); - @override - _i19.Future<_i13.GitHubComparison> compareCommits( - _i13.RepositorySlug? slug, - String? refBase, - String? refHead, - ) => - (super.noSuchMethod( - Invocation.method( - #compareCommits, - [ - slug, - refBase, - refHead, - ], - ), - returnValue: _i19.Future<_i13.GitHubComparison>.value(_FakeGitHubComparison_70( - this, - Invocation.method( - #compareCommits, - [ - slug, - refBase, - refHead, - ], - ), - )), - ) as _i19.Future<_i13.GitHubComparison>); - @override - _i19.Future<_i13.GitHubFile> getReadme( - _i13.RepositorySlug? slug, { - String? ref, - }) => - (super.noSuchMethod( - Invocation.method( - #getReadme, - [slug], - {#ref: ref}, - ), - returnValue: _i19.Future<_i13.GitHubFile>.value(_FakeGitHubFile_71( - this, - Invocation.method( - #getReadme, - [slug], - {#ref: ref}, - ), - )), - ) as _i19.Future<_i13.GitHubFile>); - @override - _i19.Future<_i13.RepositoryContents> getContents( - _i13.RepositorySlug? slug, - String? path, { - String? ref, - }) => - (super.noSuchMethod( - Invocation.method( - #getContents, - [ - slug, - path, - ], - {#ref: ref}, - ), - returnValue: _i19.Future<_i13.RepositoryContents>.value(_FakeRepositoryContents_72( - this, - Invocation.method( - #getContents, - [ - slug, - path, - ], - {#ref: ref}, - ), - )), - ) as _i19.Future<_i13.RepositoryContents>); - @override - _i19.Future<_i13.ContentCreation> createFile( - _i13.RepositorySlug? slug, - _i13.CreateFile? file, - ) => - (super.noSuchMethod( - Invocation.method( - #createFile, - [ - slug, - file, - ], - ), - returnValue: _i19.Future<_i13.ContentCreation>.value(_FakeContentCreation_73( - this, - Invocation.method( - #createFile, - [ - slug, - file, - ], - ), - )), - ) as _i19.Future<_i13.ContentCreation>); - @override - _i19.Future<_i13.ContentCreation> updateFile( - _i13.RepositorySlug? slug, - String? path, - String? message, - String? content, - String? sha, { - String? branch, - }) => - (super.noSuchMethod( - Invocation.method( - #updateFile, - [ - slug, - path, - message, - content, - sha, - ], - {#branch: branch}, - ), - returnValue: _i19.Future<_i13.ContentCreation>.value(_FakeContentCreation_73( - this, - Invocation.method( - #updateFile, - [ - slug, - path, - message, - content, - sha, - ], - {#branch: branch}, - ), - )), - ) as _i19.Future<_i13.ContentCreation>); - @override - _i19.Future<_i13.ContentCreation> deleteFile( - _i13.RepositorySlug? slug, - String? path, - String? message, - String? sha, - String? branch, - ) => - (super.noSuchMethod( - Invocation.method( - #deleteFile, - [ - slug, - path, - message, - sha, - branch, - ], - ), - returnValue: _i19.Future<_i13.ContentCreation>.value(_FakeContentCreation_73( - this, - Invocation.method( - #deleteFile, - [ - slug, - path, - message, - sha, - branch, - ], - ), - )), - ) as _i19.Future<_i13.ContentCreation>); - @override - _i19.Future getArchiveLink( - _i13.RepositorySlug? slug, - String? ref, { - String? format = r'tarball', - }) => - (super.noSuchMethod( - Invocation.method( - #getArchiveLink, - [ - slug, - ref, - ], - {#format: format}, - ), - returnValue: _i19.Future.value(), - ) as _i19.Future); - @override - _i19.Stream<_i13.Repository> listForks(_i13.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listForks, - [slug], - ), - returnValue: _i19.Stream<_i13.Repository>.empty(), - ) as _i19.Stream<_i13.Repository>); - @override - _i19.Future<_i13.Repository> createFork( - _i13.RepositorySlug? slug, [ - _i13.CreateFork? fork, - ]) => - (super.noSuchMethod( - Invocation.method( - #createFork, - [ - slug, - fork, - ], - ), - returnValue: _i19.Future<_i13.Repository>.value(_FakeRepository_64( - this, - Invocation.method( - #createFork, - [ - slug, - fork, - ], - ), - )), - ) as _i19.Future<_i13.Repository>); - @override - _i19.Stream<_i13.Hook> listHooks(_i13.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listHooks, - [slug], - ), - returnValue: _i19.Stream<_i13.Hook>.empty(), - ) as _i19.Stream<_i13.Hook>); - @override - _i19.Future<_i13.Hook> getHook( - _i13.RepositorySlug? slug, - int? id, - ) => - (super.noSuchMethod( - Invocation.method( - #getHook, - [ - slug, - id, - ], - ), - returnValue: _i19.Future<_i13.Hook>.value(_FakeHook_74( - this, - Invocation.method( - #getHook, - [ - slug, - id, - ], - ), - )), - ) as _i19.Future<_i13.Hook>); - @override - _i19.Future<_i13.Hook> createHook( - _i13.RepositorySlug? slug, - _i13.CreateHook? hook, - ) => - (super.noSuchMethod( - Invocation.method( - #createHook, - [ - slug, - hook, - ], - ), - returnValue: _i19.Future<_i13.Hook>.value(_FakeHook_74( - this, - Invocation.method( - #createHook, - [ - slug, - hook, - ], - ), - )), - ) as _i19.Future<_i13.Hook>); - @override - _i19.Future<_i13.Hook> editHook( - _i13.RepositorySlug? slug, - _i13.Hook? hookToEdit, { - String? configUrl, - String? configContentType, - String? configSecret, - bool? configInsecureSsl, - List? events, - List? addEvents, - List? removeEvents, - bool? active, - }) => - (super.noSuchMethod( - Invocation.method( - #editHook, - [ - slug, - hookToEdit, - ], - { - #configUrl: configUrl, - #configContentType: configContentType, - #configSecret: configSecret, - #configInsecureSsl: configInsecureSsl, - #events: events, - #addEvents: addEvents, - #removeEvents: removeEvents, - #active: active, - }, - ), - returnValue: _i19.Future<_i13.Hook>.value(_FakeHook_74( - this, - Invocation.method( - #editHook, - [ - slug, - hookToEdit, - ], - { - #configUrl: configUrl, - #configContentType: configContentType, - #configSecret: configSecret, - #configInsecureSsl: configInsecureSsl, - #events: events, - #addEvents: addEvents, - #removeEvents: removeEvents, - #active: active, - }, - ), - )), - ) as _i19.Future<_i13.Hook>); - @override - _i19.Future testPushHook( - _i13.RepositorySlug? slug, - int? id, - ) => - (super.noSuchMethod( - Invocation.method( - #testPushHook, - [ - slug, - id, - ], - ), - returnValue: _i19.Future.value(false), - ) as _i19.Future); - @override - _i19.Future pingHook( - _i13.RepositorySlug? slug, - int? id, - ) => - (super.noSuchMethod( - Invocation.method( - #pingHook, - [ - slug, - id, - ], - ), - returnValue: _i19.Future.value(false), - ) as _i19.Future); - @override - _i19.Future deleteHook( - _i13.RepositorySlug? slug, - int? id, - ) => - (super.noSuchMethod( - Invocation.method( - #deleteHook, - [ - slug, - id, - ], - ), - returnValue: _i19.Future.value(false), - ) as _i19.Future); - @override - _i19.Stream<_i13.PublicKey> listDeployKeys(_i13.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listDeployKeys, - [slug], - ), - returnValue: _i19.Stream<_i13.PublicKey>.empty(), - ) as _i19.Stream<_i13.PublicKey>); - @override - _i19.Future<_i13.PublicKey> getDeployKey( - _i13.RepositorySlug? slug, { - required int? id, - }) => - (super.noSuchMethod( - Invocation.method( - #getDeployKey, - [slug], - {#id: id}, - ), - returnValue: _i19.Future<_i13.PublicKey>.value(_FakePublicKey_75( - this, - Invocation.method( - #getDeployKey, - [slug], - {#id: id}, - ), - )), - ) as _i19.Future<_i13.PublicKey>); - @override - _i19.Future<_i13.PublicKey> createDeployKey( - _i13.RepositorySlug? slug, - _i13.CreatePublicKey? key, - ) => - (super.noSuchMethod( - Invocation.method( - #createDeployKey, - [ - slug, - key, - ], - ), - returnValue: _i19.Future<_i13.PublicKey>.value(_FakePublicKey_75( - this, - Invocation.method( - #createDeployKey, - [ - slug, - key, - ], - ), - )), - ) as _i19.Future<_i13.PublicKey>); - @override - _i19.Future deleteDeployKey({ - required _i13.RepositorySlug? slug, - required _i13.PublicKey? key, - }) => - (super.noSuchMethod( - Invocation.method( - #deleteDeployKey, - [], - { - #slug: slug, - #key: key, - }, - ), - returnValue: _i19.Future.value(false), - ) as _i19.Future); - @override - _i19.Future<_i13.RepositoryCommit> merge( - _i13.RepositorySlug? slug, - _i13.CreateMerge? merge, - ) => - (super.noSuchMethod( - Invocation.method( - #merge, - [ - slug, - merge, - ], - ), - returnValue: _i19.Future<_i13.RepositoryCommit>.value(_FakeRepositoryCommit_69( - this, - Invocation.method( - #merge, - [ - slug, - merge, - ], - ), - )), - ) as _i19.Future<_i13.RepositoryCommit>); - @override - _i19.Future<_i13.RepositoryPages> getPagesInfo(_i13.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #getPagesInfo, - [slug], - ), - returnValue: _i19.Future<_i13.RepositoryPages>.value(_FakeRepositoryPages_76( - this, - Invocation.method( - #getPagesInfo, - [slug], - ), - )), - ) as _i19.Future<_i13.RepositoryPages>); - @override - _i19.Stream<_i13.PageBuild> listPagesBuilds(_i13.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listPagesBuilds, - [slug], - ), - returnValue: _i19.Stream<_i13.PageBuild>.empty(), - ) as _i19.Stream<_i13.PageBuild>); - @override - _i19.Future<_i13.PageBuild> getLatestPagesBuild(_i13.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #getLatestPagesBuild, - [slug], - ), - returnValue: _i19.Future<_i13.PageBuild>.value(_FakePageBuild_77( - this, - Invocation.method( - #getLatestPagesBuild, - [slug], - ), - )), - ) as _i19.Future<_i13.PageBuild>); - @override - _i19.Stream<_i13.Release> listReleases(_i13.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listReleases, - [slug], - ), - returnValue: _i19.Stream<_i13.Release>.empty(), - ) as _i19.Stream<_i13.Release>); - @override - _i19.Future<_i13.Release> getLatestRelease(_i13.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #getLatestRelease, - [slug], - ), - returnValue: _i19.Future<_i13.Release>.value(_FakeRelease_78( - this, - Invocation.method( - #getLatestRelease, - [slug], - ), - )), - ) as _i19.Future<_i13.Release>); - @override - _i19.Future<_i13.Release> getReleaseById( - _i13.RepositorySlug? slug, - int? id, - ) => - (super.noSuchMethod( - Invocation.method( - #getReleaseById, - [ - slug, - id, - ], - ), - returnValue: _i19.Future<_i13.Release>.value(_FakeRelease_78( - this, - Invocation.method( - #getReleaseById, - [ - slug, - id, - ], - ), - )), - ) as _i19.Future<_i13.Release>); - @override - _i19.Future<_i13.Release> getReleaseByTagName( - _i13.RepositorySlug? slug, - String? tagName, - ) => - (super.noSuchMethod( - Invocation.method( - #getReleaseByTagName, - [ - slug, - tagName, - ], - ), - returnValue: _i19.Future<_i13.Release>.value(_FakeRelease_78( - this, - Invocation.method( - #getReleaseByTagName, - [ - slug, - tagName, - ], - ), - )), - ) as _i19.Future<_i13.Release>); - @override - _i19.Future<_i13.Release> createRelease( - _i13.RepositorySlug? slug, - _i13.CreateRelease? createRelease, { - bool? getIfExists = true, - }) => - (super.noSuchMethod( - Invocation.method( - #createRelease, - [ - slug, - createRelease, - ], - {#getIfExists: getIfExists}, - ), - returnValue: _i19.Future<_i13.Release>.value(_FakeRelease_78( - this, - Invocation.method( - #createRelease, - [ - slug, - createRelease, - ], - {#getIfExists: getIfExists}, - ), - )), - ) as _i19.Future<_i13.Release>); - @override - _i19.Future<_i13.Release> editRelease( - _i13.RepositorySlug? slug, - _i13.Release? releaseToEdit, { - String? tagName, - String? targetCommitish, - String? name, - String? body, - bool? draft, - bool? preRelease, - }) => - (super.noSuchMethod( - Invocation.method( - #editRelease, - [ - slug, - releaseToEdit, - ], - { - #tagName: tagName, - #targetCommitish: targetCommitish, - #name: name, - #body: body, - #draft: draft, - #preRelease: preRelease, - }, - ), - returnValue: _i19.Future<_i13.Release>.value(_FakeRelease_78( - this, - Invocation.method( - #editRelease, - [ - slug, - releaseToEdit, - ], - { - #tagName: tagName, - #targetCommitish: targetCommitish, - #name: name, - #body: body, - #draft: draft, - #preRelease: preRelease, - }, - ), - )), - ) as _i19.Future<_i13.Release>); - @override - _i19.Future deleteRelease( - _i13.RepositorySlug? slug, - _i13.Release? release, - ) => - (super.noSuchMethod( - Invocation.method( - #deleteRelease, - [ - slug, - release, - ], - ), - returnValue: _i19.Future.value(false), - ) as _i19.Future); - @override - _i19.Stream<_i13.ReleaseAsset> listReleaseAssets( - _i13.RepositorySlug? slug, - _i13.Release? release, - ) => - (super.noSuchMethod( - Invocation.method( - #listReleaseAssets, - [ - slug, - release, - ], - ), - returnValue: _i19.Stream<_i13.ReleaseAsset>.empty(), - ) as _i19.Stream<_i13.ReleaseAsset>); - @override - _i19.Future<_i13.ReleaseAsset> getReleaseAsset( - _i13.RepositorySlug? slug, - _i13.Release? release, { - required int? assetId, - }) => - (super.noSuchMethod( - Invocation.method( - #getReleaseAsset, - [ - slug, - release, - ], - {#assetId: assetId}, - ), - returnValue: _i19.Future<_i13.ReleaseAsset>.value(_FakeReleaseAsset_79( - this, - Invocation.method( - #getReleaseAsset, - [ - slug, - release, - ], - {#assetId: assetId}, - ), - )), - ) as _i19.Future<_i13.ReleaseAsset>); - @override - _i19.Future<_i13.ReleaseAsset> editReleaseAsset( - _i13.RepositorySlug? slug, - _i13.ReleaseAsset? assetToEdit, { - String? name, - String? label, - }) => - (super.noSuchMethod( - Invocation.method( - #editReleaseAsset, - [ - slug, - assetToEdit, - ], - { - #name: name, - #label: label, - }, - ), - returnValue: _i19.Future<_i13.ReleaseAsset>.value(_FakeReleaseAsset_79( - this, - Invocation.method( - #editReleaseAsset, - [ - slug, - assetToEdit, - ], - { - #name: name, - #label: label, - }, - ), - )), - ) as _i19.Future<_i13.ReleaseAsset>); - @override - _i19.Future deleteReleaseAsset( - _i13.RepositorySlug? slug, - _i13.ReleaseAsset? asset, - ) => - (super.noSuchMethod( - Invocation.method( - #deleteReleaseAsset, - [ - slug, - asset, - ], - ), - returnValue: _i19.Future.value(false), - ) as _i19.Future); - @override - _i19.Future> uploadReleaseAssets( - _i13.Release? release, - Iterable<_i13.CreateReleaseAsset>? createReleaseAssets, - ) => - (super.noSuchMethod( - Invocation.method( - #uploadReleaseAssets, - [ - release, - createReleaseAssets, - ], - ), - returnValue: _i19.Future>.value(<_i13.ReleaseAsset>[]), - ) as _i19.Future>); - @override - _i19.Future> listContributorStats(_i13.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listContributorStats, - [slug], - ), - returnValue: _i19.Future>.value(<_i13.ContributorStatistics>[]), - ) as _i19.Future>); - @override - _i19.Stream<_i13.YearCommitCountWeek> listCommitActivity(_i13.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listCommitActivity, - [slug], - ), - returnValue: _i19.Stream<_i13.YearCommitCountWeek>.empty(), - ) as _i19.Stream<_i13.YearCommitCountWeek>); - @override - _i19.Stream<_i13.WeeklyChangesCount> listCodeFrequency(_i13.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listCodeFrequency, - [slug], - ), - returnValue: _i19.Stream<_i13.WeeklyChangesCount>.empty(), - ) as _i19.Stream<_i13.WeeklyChangesCount>); - @override - _i19.Future<_i13.ContributorParticipation> getParticipation(_i13.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #getParticipation, - [slug], - ), - returnValue: _i19.Future<_i13.ContributorParticipation>.value(_FakeContributorParticipation_80( - this, - Invocation.method( - #getParticipation, - [slug], - ), - )), - ) as _i19.Future<_i13.ContributorParticipation>); - @override - _i19.Stream<_i13.PunchcardEntry> listPunchcard(_i13.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listPunchcard, - [slug], - ), - returnValue: _i19.Stream<_i13.PunchcardEntry>.empty(), - ) as _i19.Stream<_i13.PunchcardEntry>); - @override - _i19.Stream<_i13.RepositoryStatus> listStatuses( - _i13.RepositorySlug? slug, - String? ref, - ) => - (super.noSuchMethod( - Invocation.method( - #listStatuses, - [ - slug, - ref, - ], - ), - returnValue: _i19.Stream<_i13.RepositoryStatus>.empty(), - ) as _i19.Stream<_i13.RepositoryStatus>); - @override - _i19.Future<_i13.RepositoryStatus> createStatus( - _i13.RepositorySlug? slug, - String? ref, - _i13.CreateStatus? request, - ) => - (super.noSuchMethod( - Invocation.method( - #createStatus, - [ - slug, - ref, - request, - ], - ), - returnValue: _i19.Future<_i13.RepositoryStatus>.value(_FakeRepositoryStatus_81( - this, - Invocation.method( - #createStatus, - [ - slug, - ref, - request, - ], - ), - )), - ) as _i19.Future<_i13.RepositoryStatus>); - @override - _i19.Future<_i13.CombinedRepositoryStatus> getCombinedStatus( - _i13.RepositorySlug? slug, - String? ref, - ) => - (super.noSuchMethod( - Invocation.method( - #getCombinedStatus, - [ - slug, - ref, - ], - ), - returnValue: _i19.Future<_i13.CombinedRepositoryStatus>.value(_FakeCombinedRepositoryStatus_82( - this, - Invocation.method( - #getCombinedStatus, - [ - slug, - ref, - ], - ), - )), - ) as _i19.Future<_i13.CombinedRepositoryStatus>); - @override - _i19.Future<_i13.ReleaseNotes> generateReleaseNotes(_i13.CreateReleaseNotes? crn) => (super.noSuchMethod( - Invocation.method( - #generateReleaseNotes, - [crn], - ), - returnValue: _i19.Future<_i13.ReleaseNotes>.value(_FakeReleaseNotes_83( - this, - Invocation.method( - #generateReleaseNotes, - [crn], - ), - )), - ) as _i19.Future<_i13.ReleaseNotes>); -} - -/// A class which mocks [SearchService]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockSearchService extends _i1.Mock implements _i13.SearchService { - MockSearchService() { - _i1.throwOnMissingStub(this); - } - - @override - _i13.GitHub get github => (super.noSuchMethod( - Invocation.getter(#github), - returnValue: _FakeGitHub_16( - this, - Invocation.getter(#github), - ), - ) as _i13.GitHub); - @override - _i19.Stream<_i13.Repository> repositories( - String? query, { - String? sort, - int? pages = 2, - }) => - (super.noSuchMethod( - Invocation.method( - #repositories, - [query], - { - #sort: sort, - #pages: pages, - }, - ), - returnValue: _i19.Stream<_i13.Repository>.empty(), - ) as _i19.Stream<_i13.Repository>); - @override - _i19.Stream<_i13.CodeSearchResults> code( - String? query, { - int? pages, - int? perPage, - String? language, - String? filename, - String? extension, - String? user, - String? org, - String? repo, - String? fork, - String? path, - String? size, - bool? inFile = true, - bool? inPath = false, - }) => - (super.noSuchMethod( - Invocation.method( - #code, - [query], - { - #pages: pages, - #perPage: perPage, - #language: language, - #filename: filename, - #extension: extension, - #user: user, - #org: org, - #repo: repo, - #fork: fork, - #path: path, - #size: size, - #inFile: inFile, - #inPath: inPath, - }, - ), - returnValue: _i19.Stream<_i13.CodeSearchResults>.empty(), - ) as _i19.Stream<_i13.CodeSearchResults>); - @override - _i19.Stream<_i13.Issue> issues( - String? query, { - String? sort, - int? pages = 2, - }) => - (super.noSuchMethod( - Invocation.method( - #issues, - [query], - { - #sort: sort, - #pages: pages, - }, - ), - returnValue: _i19.Stream<_i13.Issue>.empty(), - ) as _i19.Stream<_i13.Issue>); - @override - _i19.Stream<_i13.User> users( - String? query, { - String? sort, - int? pages = 2, - int? perPage = 30, - }) => - (super.noSuchMethod( - Invocation.method( - #users, - [query], - { - #sort: sort, - #pages: pages, - #perPage: perPage, - }, - ), - returnValue: _i19.Stream<_i13.User>.empty(), - ) as _i19.Stream<_i13.User>); -} - -/// A class which mocks [TabledataResource]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockTabledataResource extends _i1.Mock implements _i6.TabledataResource { - MockTabledataResource() { - _i1.throwOnMissingStub(this); - } - - @override - _i19.Future<_i6.TableDataInsertAllResponse> insertAll( - _i6.TableDataInsertAllRequest? request, - String? projectId, - String? datasetId, - String? tableId, { - String? $fields, - }) => - (super.noSuchMethod( - Invocation.method( - #insertAll, - [ - request, - projectId, - datasetId, - tableId, - ], - {#$fields: $fields}, - ), - returnValue: _i19.Future<_i6.TableDataInsertAllResponse>.value(_FakeTableDataInsertAllResponse_84( - this, - Invocation.method( - #insertAll, - [ - request, - projectId, - datasetId, - tableId, - ], - {#$fields: $fields}, - ), - )), - ) as _i19.Future<_i6.TableDataInsertAllResponse>); - @override - _i19.Future<_i6.TableDataList> list( - String? projectId, - String? datasetId, - String? tableId, { - int? maxResults, - String? pageToken, - String? selectedFields, - String? startIndex, - String? $fields, - }) => - (super.noSuchMethod( - Invocation.method( - #list, - [ - projectId, - datasetId, - tableId, - ], - { - #maxResults: maxResults, - #pageToken: pageToken, - #selectedFields: selectedFields, - #startIndex: startIndex, - #$fields: $fields, - }, - ), - returnValue: _i19.Future<_i6.TableDataList>.value(_FakeTableDataList_85( - this, - Invocation.method( - #list, - [ - projectId, - datasetId, - tableId, - ], - { - #maxResults: maxResults, - #pageToken: pageToken, - #selectedFields: selectedFields, - #startIndex: startIndex, - #$fields: $fields, - }, - ), - )), - ) as _i19.Future<_i6.TableDataList>); -} - -/// A class which mocks [UsersService]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockUsersService extends _i1.Mock implements _i13.UsersService { - MockUsersService() { - _i1.throwOnMissingStub(this); - } - - @override - _i13.GitHub get github => (super.noSuchMethod( - Invocation.getter(#github), - returnValue: _FakeGitHub_16( - this, - Invocation.getter(#github), - ), - ) as _i13.GitHub); - @override - _i19.Future<_i13.User> getUser(String? name) => (super.noSuchMethod( - Invocation.method( - #getUser, - [name], - ), - returnValue: _i19.Future<_i13.User>.value(_FakeUser_86( - this, - Invocation.method( - #getUser, - [name], - ), - )), - ) as _i19.Future<_i13.User>); - @override - _i19.Future<_i13.CurrentUser> editCurrentUser({ - String? name, - String? email, - String? blog, - String? company, - String? location, - bool? hireable, - String? bio, - }) => - (super.noSuchMethod( - Invocation.method( - #editCurrentUser, - [], - { - #name: name, - #email: email, - #blog: blog, - #company: company, - #location: location, - #hireable: hireable, - #bio: bio, - }, - ), - returnValue: _i19.Future<_i13.CurrentUser>.value(_FakeCurrentUser_87( - this, - Invocation.method( - #editCurrentUser, - [], - { - #name: name, - #email: email, - #blog: blog, - #company: company, - #location: location, - #hireable: hireable, - #bio: bio, - }, - ), - )), - ) as _i19.Future<_i13.CurrentUser>); - @override - _i19.Stream<_i13.User> getUsers( - List? names, { - int? pages, - }) => - (super.noSuchMethod( - Invocation.method( - #getUsers, - [names], - {#pages: pages}, - ), - returnValue: _i19.Stream<_i13.User>.empty(), - ) as _i19.Stream<_i13.User>); - @override - _i19.Future<_i13.CurrentUser> getCurrentUser() => (super.noSuchMethod( - Invocation.method( - #getCurrentUser, - [], - ), - returnValue: _i19.Future<_i13.CurrentUser>.value(_FakeCurrentUser_87( - this, - Invocation.method( - #getCurrentUser, - [], - ), - )), - ) as _i19.Future<_i13.CurrentUser>); - @override - _i19.Future isUser(String? name) => (super.noSuchMethod( - Invocation.method( - #isUser, - [name], - ), - returnValue: _i19.Future.value(false), - ) as _i19.Future); - @override - _i19.Stream<_i13.User> listUsers({ - int? pages, - int? since, - }) => - (super.noSuchMethod( - Invocation.method( - #listUsers, - [], - { - #pages: pages, - #since: since, - }, - ), - returnValue: _i19.Stream<_i13.User>.empty(), - ) as _i19.Stream<_i13.User>); - @override - _i19.Stream<_i13.UserEmail> listEmails() => (super.noSuchMethod( - Invocation.method( - #listEmails, - [], - ), - returnValue: _i19.Stream<_i13.UserEmail>.empty(), - ) as _i19.Stream<_i13.UserEmail>); - @override - _i19.Stream<_i13.UserEmail> addEmails(List? emails) => (super.noSuchMethod( - Invocation.method( - #addEmails, - [emails], - ), - returnValue: _i19.Stream<_i13.UserEmail>.empty(), - ) as _i19.Stream<_i13.UserEmail>); - @override - _i19.Future deleteEmails(List? emails) => (super.noSuchMethod( - Invocation.method( - #deleteEmails, - [emails], - ), - returnValue: _i19.Future.value(false), - ) as _i19.Future); - @override - _i19.Stream<_i13.User> listUserFollowers(String? user) => (super.noSuchMethod( - Invocation.method( - #listUserFollowers, - [user], - ), - returnValue: _i19.Stream<_i13.User>.empty(), - ) as _i19.Stream<_i13.User>); - @override - _i19.Future isFollowingUser(String? user) => (super.noSuchMethod( - Invocation.method( - #isFollowingUser, - [user], - ), - returnValue: _i19.Future.value(false), - ) as _i19.Future); - @override - _i19.Future isUserFollowing( - String? user, - String? target, - ) => - (super.noSuchMethod( - Invocation.method( - #isUserFollowing, - [ - user, - target, - ], - ), - returnValue: _i19.Future.value(false), - ) as _i19.Future); - @override - _i19.Future followUser(String? user) => (super.noSuchMethod( - Invocation.method( - #followUser, - [user], - ), - returnValue: _i19.Future.value(false), - ) as _i19.Future); - @override - _i19.Future unfollowUser(String? user) => (super.noSuchMethod( - Invocation.method( - #unfollowUser, - [user], - ), - returnValue: _i19.Future.value(false), - ) as _i19.Future); - @override - _i19.Stream<_i13.User> listCurrentUserFollowers() => (super.noSuchMethod( - Invocation.method( - #listCurrentUserFollowers, - [], - ), - returnValue: _i19.Stream<_i13.User>.empty(), - ) as _i19.Stream<_i13.User>); - @override - _i19.Stream<_i13.User> listCurrentUserFollowing() => (super.noSuchMethod( - Invocation.method( - #listCurrentUserFollowing, - [], - ), - returnValue: _i19.Stream<_i13.User>.empty(), - ) as _i19.Stream<_i13.User>); - @override - _i19.Stream<_i13.PublicKey> listPublicKeys([String? userLogin]) => (super.noSuchMethod( - Invocation.method( - #listPublicKeys, - [userLogin], - ), - returnValue: _i19.Stream<_i13.PublicKey>.empty(), - ) as _i19.Stream<_i13.PublicKey>); - @override - _i19.Future<_i13.PublicKey> createPublicKey(_i13.CreatePublicKey? key) => (super.noSuchMethod( - Invocation.method( - #createPublicKey, - [key], - ), - returnValue: _i19.Future<_i13.PublicKey>.value(_FakePublicKey_75( - this, - Invocation.method( - #createPublicKey, - [key], - ), - )), - ) as _i19.Future<_i13.PublicKey>); -} - -/// A class which mocks [Cache]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockCache extends _i1.Mock implements _i24.Cache<_i35.Uint8List> { - MockCache() { - _i1.throwOnMissingStub(this); - } - - @override - _i24.Entry<_i35.Uint8List> operator [](String? key) => (super.noSuchMethod( - Invocation.method( - #[], - [key], - ), - returnValue: _FakeEntry_88<_i35.Uint8List>( - this, - Invocation.method( - #[], - [key], - ), - ), - ) as _i24.Entry<_i35.Uint8List>); - @override - _i24.Cache<_i35.Uint8List> withPrefix(String? prefix) => (super.noSuchMethod( - Invocation.method( - #withPrefix, - [prefix], - ), - returnValue: _FakeCache_89<_i35.Uint8List>( - this, - Invocation.method( - #withPrefix, - [prefix], - ), - ), - ) as _i24.Cache<_i35.Uint8List>); - @override - _i24.Cache withCodec(_i22.Codec? codec) => (super.noSuchMethod( - Invocation.method( - #withCodec, - [codec], - ), - returnValue: _FakeCache_89( - this, - Invocation.method( - #withCodec, - [codec], - ), - ), - ) as _i24.Cache); - @override - _i24.Cache<_i35.Uint8List> withTTL(Duration? ttl) => (super.noSuchMethod( - Invocation.method( - #withTTL, - [ttl], - ), - returnValue: _FakeCache_89<_i35.Uint8List>( - this, - Invocation.method( - #withTTL, - [ttl], - ), - ), - ) as _i24.Cache<_i35.Uint8List>); -} - -/// A class which mocks [GitHub]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockGitHub extends _i1.Mock implements _i13.GitHub { - MockGitHub() { - _i1.throwOnMissingStub(this); - } - - @override - _i13.Authentication get auth => (super.noSuchMethod( - Invocation.getter(#auth), - returnValue: _FakeAuthentication_90( - this, - Invocation.getter(#auth), - ), - ) as _i13.Authentication); - @override - set auth(_i13.Authentication? _auth) => super.noSuchMethod( - Invocation.setter( - #auth, - _auth, - ), - returnValueForMissingStub: null, - ); - @override - String get endpoint => (super.noSuchMethod( - Invocation.getter(#endpoint), - returnValue: '', - ) as String); - @override - String get version => (super.noSuchMethod( - Invocation.getter(#version), - returnValue: '', - ) as String); - @override - _i2.Client get client => (super.noSuchMethod( - Invocation.getter(#client), - returnValue: _FakeClient_0( - this, - Invocation.getter(#client), - ), - ) as _i2.Client); - @override - _i13.ActivityService get activity => (super.noSuchMethod( - Invocation.getter(#activity), - returnValue: _FakeActivityService_91( - this, - Invocation.getter(#activity), - ), - ) as _i13.ActivityService); - @override - _i13.AuthorizationsService get authorizations => (super.noSuchMethod( - Invocation.getter(#authorizations), - returnValue: _FakeAuthorizationsService_92( - this, - Invocation.getter(#authorizations), - ), - ) as _i13.AuthorizationsService); - @override - _i13.GistsService get gists => (super.noSuchMethod( - Invocation.getter(#gists), - returnValue: _FakeGistsService_93( - this, - Invocation.getter(#gists), - ), - ) as _i13.GistsService); - @override - _i13.GitService get git => (super.noSuchMethod( - Invocation.getter(#git), - returnValue: _FakeGitService_94( - this, - Invocation.getter(#git), - ), - ) as _i13.GitService); - @override - _i13.IssuesService get issues => (super.noSuchMethod( - Invocation.getter(#issues), - returnValue: _FakeIssuesService_95( - this, - Invocation.getter(#issues), - ), - ) as _i13.IssuesService); - @override - _i13.MiscService get misc => (super.noSuchMethod( - Invocation.getter(#misc), - returnValue: _FakeMiscService_96( - this, - Invocation.getter(#misc), - ), - ) as _i13.MiscService); - @override - _i13.OrganizationsService get organizations => (super.noSuchMethod( - Invocation.getter(#organizations), - returnValue: _FakeOrganizationsService_97( - this, - Invocation.getter(#organizations), - ), - ) as _i13.OrganizationsService); - @override - _i13.PullRequestsService get pullRequests => (super.noSuchMethod( - Invocation.getter(#pullRequests), - returnValue: _FakePullRequestsService_98( - this, - Invocation.getter(#pullRequests), - ), - ) as _i13.PullRequestsService); - @override - _i13.RepositoriesService get repositories => (super.noSuchMethod( - Invocation.getter(#repositories), - returnValue: _FakeRepositoriesService_99( - this, - Invocation.getter(#repositories), - ), - ) as _i13.RepositoriesService); - @override - _i13.SearchService get search => (super.noSuchMethod( - Invocation.getter(#search), - returnValue: _FakeSearchService_100( - this, - Invocation.getter(#search), - ), - ) as _i13.SearchService); - @override - _i13.UrlShortenerService get urlShortener => (super.noSuchMethod( - Invocation.getter(#urlShortener), - returnValue: _FakeUrlShortenerService_101( - this, - Invocation.getter(#urlShortener), - ), - ) as _i13.UrlShortenerService); - @override - _i13.UsersService get users => (super.noSuchMethod( - Invocation.getter(#users), - returnValue: _FakeUsersService_102( - this, - Invocation.getter(#users), - ), - ) as _i13.UsersService); - @override - _i13.ChecksService get checks => (super.noSuchMethod( - Invocation.getter(#checks), - returnValue: _FakeChecksService_103( - this, - Invocation.getter(#checks), - ), - ) as _i13.ChecksService); - @override - _i19.Future getJSON( - String? path, { - int? statusCode, - void Function(_i2.Response)? fail, - Map? headers, - Map? params, - _i13.JSONConverter? convert, - String? preview, - }) => - (super.noSuchMethod( - Invocation.method( - #getJSON, - [path], - { - #statusCode: statusCode, - #fail: fail, - #headers: headers, - #params: params, - #convert: convert, - #preview: preview, - }, - ), - returnValue: _i27.ifNotNull( - _i27.dummyValueOrNull( - this, - Invocation.method( - #getJSON, - [path], - { - #statusCode: statusCode, - #fail: fail, - #headers: headers, - #params: params, - #convert: convert, - #preview: preview, - }, - ), - ), - (T v) => _i19.Future.value(v), - ) ?? - _FakeFuture_22( - this, - Invocation.method( - #getJSON, - [path], - { - #statusCode: statusCode, - #fail: fail, - #headers: headers, - #params: params, - #convert: convert, - #preview: preview, - }, - ), - ), - ) as _i19.Future); - @override - _i19.Future postJSON( - String? path, { - int? statusCode, - void Function(_i2.Response)? fail, - Map? headers, - Map? params, - _i13.JSONConverter? convert, - dynamic body, - String? preview, - }) => - (super.noSuchMethod( - Invocation.method( - #postJSON, - [path], - { - #statusCode: statusCode, - #fail: fail, - #headers: headers, - #params: params, - #convert: convert, - #body: body, - #preview: preview, - }, - ), - returnValue: _i40.postJsonShim( - path, - statusCode: statusCode, - fail: fail, - headers: headers, - params: params, - convert: convert, - body: body, - preview: preview, - ), - ) as _i19.Future); - @override - _i19.Future putJSON( - String? path, { - int? statusCode, - void Function(_i2.Response)? fail, - Map? headers, - Map? params, - _i13.JSONConverter? convert, - dynamic body, - String? preview, - }) => - (super.noSuchMethod( - Invocation.method( - #putJSON, - [path], - { - #statusCode: statusCode, - #fail: fail, - #headers: headers, - #params: params, - #convert: convert, - #body: body, - #preview: preview, - }, - ), - returnValue: _i27.ifNotNull( - _i27.dummyValueOrNull( - this, - Invocation.method( - #putJSON, - [path], - { - #statusCode: statusCode, - #fail: fail, - #headers: headers, - #params: params, - #convert: convert, - #body: body, - #preview: preview, - }, - ), - ), - (T v) => _i19.Future.value(v), - ) ?? - _FakeFuture_22( - this, - Invocation.method( - #putJSON, - [path], - { - #statusCode: statusCode, - #fail: fail, - #headers: headers, - #params: params, - #convert: convert, - #body: body, - #preview: preview, - }, - ), - ), - ) as _i19.Future); - @override - _i19.Future patchJSON( - String? path, { - int? statusCode, - void Function(_i2.Response)? fail, - Map? headers, - Map? params, - _i13.JSONConverter? convert, - dynamic body, - String? preview, - }) => - (super.noSuchMethod( - Invocation.method( - #patchJSON, - [path], - { - #statusCode: statusCode, - #fail: fail, - #headers: headers, - #params: params, - #convert: convert, - #body: body, - #preview: preview, - }, - ), - returnValue: _i27.ifNotNull( - _i27.dummyValueOrNull( - this, - Invocation.method( - #patchJSON, - [path], - { - #statusCode: statusCode, - #fail: fail, - #headers: headers, - #params: params, - #convert: convert, - #body: body, - #preview: preview, - }, - ), - ), - (T v) => _i19.Future.value(v), - ) ?? - _FakeFuture_22( - this, - Invocation.method( - #patchJSON, - [path], - { - #statusCode: statusCode, - #fail: fail, - #headers: headers, - #params: params, - #convert: convert, - #body: body, - #preview: preview, - }, - ), - ), - ) as _i19.Future); - @override - _i19.Future requestJson( - String? method, - String? path, { - int? statusCode, - void Function(_i2.Response)? fail, - Map? headers, - Map? params, - _i13.JSONConverter? convert, - dynamic body, - String? preview, - }) => - (super.noSuchMethod( - Invocation.method( - #requestJson, - [ - method, - path, - ], - { - #statusCode: statusCode, - #fail: fail, - #headers: headers, - #params: params, - #convert: convert, - #body: body, - #preview: preview, - }, - ), - returnValue: _i27.ifNotNull( - _i27.dummyValueOrNull( - this, - Invocation.method( - #requestJson, - [ - method, - path, - ], - { - #statusCode: statusCode, - #fail: fail, - #headers: headers, - #params: params, - #convert: convert, - #body: body, - #preview: preview, - }, - ), - ), - (T v) => _i19.Future.value(v), - ) ?? - _FakeFuture_22( - this, - Invocation.method( - #requestJson, - [ - method, - path, - ], - { - #statusCode: statusCode, - #fail: fail, - #headers: headers, - #params: params, - #convert: convert, - #body: body, - #preview: preview, - }, - ), - ), - ) as _i19.Future); - @override - _i19.Future<_i2.Response> request( - String? method, - String? path, { - Map? headers, - Map? params, - dynamic body, - int? statusCode, - void Function(_i2.Response)? fail, - String? preview, - }) => - (super.noSuchMethod( - Invocation.method( - #request, - [ - method, - path, - ], - { - #headers: headers, - #params: params, - #body: body, - #statusCode: statusCode, - #fail: fail, - #preview: preview, - }, - ), - returnValue: _i19.Future<_i2.Response>.value(_FakeResponse_104( - this, - Invocation.method( - #request, - [ - method, - path, - ], - { - #headers: headers, - #params: params, - #body: body, - #statusCode: statusCode, - #fail: fail, - #preview: preview, - }, - ), - )), - ) as _i19.Future<_i2.Response>); - @override - Never handleStatusCode(_i2.Response? response) => (super.noSuchMethod( - Invocation.method( - #handleStatusCode, - [response], - ), - returnValue: null, - ) as Never); - @override - void dispose() => super.noSuchMethod( - Invocation.method( - #dispose, - [], - ), - returnValueForMissingStub: null, - ); -} diff --git a/app_dart/test/src/utilities/push_message.dart b/app_dart/test/src/utilities/push_message.dart deleted file mode 100644 index d3f2236a8..000000000 --- a/app_dart/test/src/utilities/push_message.dart +++ /dev/null @@ -1,197 +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 'package:cocoon_service/src/model/luci/push_message.dart'; - -const String ref = 'deadbeef'; - -PushMessage createBuildbucketPushMessage( - String status, { - String? result, - String builderName = 'Linux Coverage', - String urlParam = '', - int retries = 0, - String? failureReason, - String? userData = '', -}) { - return PushMessage( - data: buildPushMessageJson( - status, - result: result, - builderName: builderName, - urlParam: urlParam, - retries: retries, - failureReason: failureReason, - userData: userData, - ), - messageId: '123', - ); -} - -PushMessage pushMessageJsonNoBuildset( - String status, { - String? result, - String builderName = 'Linux Coverage', - String urlParam = '', - int retries = 0, - String? failureReason, -}) { - return PushMessage( - data: buildPushMessageJsonNoBuildset( - status, - result: result, - builderName: builderName, - urlParam: urlParam, - retries: retries, - failureReason: failureReason, - ), - messageId: '123', - ); -} - -String buildPushMessageJson( - String status, { - String? result, - String builderName = 'Linux Coverage', - String urlParam = '', - int retries = 0, - String? failureReason, - String? userData, -}) => - base64.encode( - utf8.encode( - buildPushMessageString( - status, - result: result, - builderName: builderName, - urlParam: urlParam, - retries: retries, - failureReason: failureReason, - userData: userData, - ), - ), - ); - -String buildPushMessageJsonNoBuildset( - String status, { - String? result, - String builderName = 'Linux Coverage', - String urlParam = '', - int retries = 0, - String? failureReason, -}) => - base64.encode( - utf8.encode( - buildPushMessageNoBuildsetString( - status, - result: result, - builderName: builderName, - urlParam: urlParam, - retries: retries, - failureReason: failureReason, - ), - ), - ); - -String buildPushMessageString( - String status, { - String? result, - String builderName = 'Linux Coverage', - String urlParam = '', - int retries = 0, - String? failureReason, - String? userData, -}) { - return '''{ - "build": { - "bucket": "luci.flutter.prod", - "canary": false, - "canary_preference": "PROD", - "created_by": "user:dnfield@google.com", - "created_ts": "1565049186247524", - "completed_ts": "1565049193786090", - "experimental": false, - ${failureReason != null ? '"failure_reason": "$failureReason",' : ''} - "id": "8905920700440101120", - "parameters_json": "{\\"builder_name\\": \\"$builderName\\", \\"properties\\": {\\"git_ref\\": \\"refs/pull/37647/head\\", \\"git_url\\": \\"https://github.com/flutter/flutter\\", \\"cores\\": [8]}}", - "project": "flutter", - ${result != null ? '"result": "$result",' : ''} - "result_details_json": "{\\"properties\\": {}, \\"swarming\\": {\\"bot_dimensions\\": {\\"caches\\": [\\"flutter_openjdk_install\\", \\"git\\", \\"goma_v2\\", \\"vpython\\"], \\"cores\\": [\\"8\\"], \\"cpu\\": [\\"x86\\", \\"x86-64\\", \\"x86-64-Broadwell_GCE\\", \\"x86-64-avx2\\"], \\"gce\\": [\\"1\\"], \\"gpu\\": [\\"none\\"], \\"id\\": [\\"luci-flutter-prod-xenial-2-bnrz\\"], \\"image\\": [\\"chrome-xenial-19052201-9cb74617499\\"], \\"inside_docker\\": [\\"0\\"], \\"kvm\\": [\\"1\\"], \\"locale\\": [\\"en_US.UTF-8\\"], \\"machine_type\\": [\\"n1-standard-8\\"], \\"os\\": [\\"Linux\\", \\"Ubuntu\\", \\"Ubuntu-16.04\\"], \\"pool\\": [\\"luci.flutter.prod\\"], \\"python\\": [\\"2.7.12\\"], \\"server_version\\": [\\"4382-5929880\\"], \\"ssd\\": [\\"0\\"], \\"zone\\": [\\"us\\", \\"us-central\\", \\"us-central1\\", \\"us-central1-c\\"]}}}", - "service_account": "flutter-prod-builder@chops-service-accounts.iam.gserviceaccount.com", - "started_ts": "1565049193786080", - "status": "$status", - "status_changed_ts": "1565049194386647", - "tags": [ - "build_address:luci.flutter.prod/$builderName/1698", - "builder:$builderName", - "buildset:pr/git/37647", - "buildset:sha/git/$ref", - "github_link:https://github.com/flutter/flutter/pull/37647", - "swarming_hostname:chromium-swarm.appspot.com", - "swarming_tag:log_location:logdog://logs.chromium.org/flutter/buildbucket/cr-buildbucket.appspot.com/8905920700440101120/+/annotations", - "swarming_tag:luci_project:flutter", - "swarming_tag:os:Linux", - "swarming_tag:recipe_name:flutter/flutter", - "swarming_tag:recipe_package:infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build", - "swarming_task_id:467d04f2f022d510", - "user_agent:flutter-cocoon" - ], - "updated_ts": "1565049194391321", - "url": "https://ci.chromium.org/b/8905920700440101120$urlParam", - "utcnow_ts": "1565049194653640" - }, - ${userData != null ? '"user_data": "$userData",' : ''} - "hostname": "cr-buildbucket.appspot.com" -}'''; -} - -String buildPushMessageNoBuildsetString( - String status, { - String? result, - String builderName = 'Linux Coverage', - String urlParam = '', - int retries = 0, - String? failureReason, -}) { - return '''{ - "build": { - "bucket": "luci.flutter.prod", - "canary": false, - "canary_preference": "PROD", - "created_by": "user:dnfield@google.com", - "created_ts": "1565049186247524", - "experimental": false, - ${failureReason != null ? '"failure_reason": "$failureReason",' : ''} - "id": "8905920700440101120", - "parameters_json": "{\\"builder_name\\": \\"$builderName\\", \\"properties\\": {\\"git_ref\\": \\"refs/pull/37647/head\\", \\"git_url\\": \\"https://github.com/flutter/flutter\\"}}", - "project": "flutter", - ${result != null ? '"result": "$result",' : ''} - "result_details_json": "{\\"properties\\": {}, \\"swarming\\": {\\"bot_dimensions\\": {\\"caches\\": [\\"flutter_openjdk_install\\", \\"git\\", \\"goma_v2\\", \\"vpython\\"], \\"cores\\": [\\"8\\"], \\"cpu\\": [\\"x86\\", \\"x86-64\\", \\"x86-64-Broadwell_GCE\\", \\"x86-64-avx2\\"], \\"gce\\": [\\"1\\"], \\"gpu\\": [\\"none\\"], \\"id\\": [\\"luci-flutter-prod-xenial-2-bnrz\\"], \\"image\\": [\\"chrome-xenial-19052201-9cb74617499\\"], \\"inside_docker\\": [\\"0\\"], \\"kvm\\": [\\"1\\"], \\"locale\\": [\\"en_US.UTF-8\\"], \\"machine_type\\": [\\"n1-standard-8\\"], \\"os\\": [\\"Linux\\", \\"Ubuntu\\", \\"Ubuntu-16.04\\"], \\"pool\\": [\\"luci.flutter.prod\\"], \\"python\\": [\\"2.7.12\\"], \\"server_version\\": [\\"4382-5929880\\"], \\"ssd\\": [\\"0\\"], \\"zone\\": [\\"us\\", \\"us-central\\", \\"us-central1\\", \\"us-central1-c\\"]}}}", - "service_account": "flutter-prod-builder@chops-service-accounts.iam.gserviceaccount.com", - "started_ts": "1565049193786080", - "status": "$status", - "status_changed_ts": "1565049194386647", - "tags": [ - "build_address:luci.flutter.prod/$builderName/1698", - "builder:$builderName", - "github_link:https://github.com/flutter/flutter/pull/37647", - "swarming_hostname:chromium-swarm.appspot.com", - "swarming_tag:log_location:logdog://logs.chromium.org/flutter/buildbucket/cr-buildbucket.appspot.com/8905920700440101120/+/annotations", - "swarming_tag:luci_project:flutter", - "swarming_tag:os:Linux", - "swarming_tag:recipe_name:flutter/flutter", - "swarming_tag:recipe_package:infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build", - "swarming_task_id:467d04f2f022d510", - "user_agent:flutter-cocoon" - ], - "updated_ts": "1565049194391321", - "url": "https://ci.chromium.org/b/8905920700440101120$urlParam", - "utcnow_ts": "1565049194653640" - }, - "hostname": "cr-buildbucket.appspot.com", - "user_data": "{\\"retries\\": $retries}" -}'''; -} diff --git a/app_dart/test/src/utilities/webhook_generators.dart b/app_dart/test/src/utilities/webhook_generators.dart deleted file mode 100644 index aa9c9a281..000000000 --- a/app_dart/test/src/utilities/webhook_generators.dart +++ /dev/null @@ -1,1072 +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/protos.dart' as pb; -import 'package:cocoon_service/src/model/luci/push_message.dart'; -import 'package:cocoon_service/src/service/config.dart'; -import 'package:github/github.dart'; -import 'package:github/hooks.dart'; - -PushMessage generateGithubWebhookMessage({ - String event = 'pull_request', - String action = 'merged', - int number = 123, - String? baseRef, - String baseSha = '4cd12fc8b7d4cc2d8609182e1c4dea5cddc86890', - String login = 'dash', - String headRef = 'abc', - bool isDraft = false, - bool merged = false, - bool mergeable = true, - String mergeCommitSha = 'fd6b46416c18de36ce87d0241994b2da180cab4c', - RepositorySlug? slug, -}) { - final String data = (pb.GithubWebhookMessage.create() - ..event = event - ..payload = _generatePullRequestEvent( - action, - number, - baseRef, - baseSha: baseSha, - login: login, - headRef: headRef, - isDraft: isDraft, - merged: merged, - isMergeable: mergeable, - slug: slug, - mergeCommitSha: mergeCommitSha, - )) - .writeToJson(); - return PushMessage(data: data, messageId: 'abc123'); -} - -String _generatePullRequestEvent( - String action, - int number, - String? baseRef, { - RepositorySlug? slug, - String login = 'flutter', - String baseSha = '4cd12fc8b7d4cc2d8609182e1c4dea5cddc86890', - String headRef = 'wait_for_reassemble', - bool includeCqLabel = false, - bool isDraft = false, - bool merged = false, - bool isMergeable = true, - String mergeCommitSha = 'fd6b46416c18de36ce87d0241994b2da180cab4c', -}) { - slug ??= Config.flutterSlug; - baseRef ??= Config.defaultBranch(slug); - return '''{ - "action": "$action", - "number": $number, - "pull_request": { - "url": "https://api.github.com/repos/${slug.fullName}/pulls/$number", - "id": 294034, - "node_id": "MDExOlB1bGxSZXF1ZXN0Mjk0MDMzODQx", - "html_url": "https://github.com/${slug.fullName}/pull/$number", - "diff_url": "https://github.com/${slug.fullName}/pull/$number.diff", - "patch_url": "https://github.com/${slug.fullName}/pull/$number.patch", - "issue_url": "https://api.github.com/repos/${slug.fullName}/issues/$number", - "number": $number, - "state": "open", - "locked": false, - "title": "Defer reassemble until reload is finished", - "user": { - "login": "$login", - "id": 862741, - "node_id": "MDQ6VXNlcjg2MjA3NDE=", - "avatar_url": "https://avatars3.githubusercontent.com/u/8620741?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/flutter", - "html_url": "https://github.com/flutter", - "followers_url": "https://api.github.com/users/flutter/followers", - "following_url": "https://api.github.com/users/flutter/following{/other_user}", - "gists_url": "https://api.github.com/users/flutter/gists{/gist_id}", - "starred_url": "https://api.github.com/users/flutter/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/flutter/subscriptions", - "organizations_url": "https://api.github.com/users/flutter/orgs", - "repos_url": "https://api.github.com/users/flutter/repos", - "events_url": "https://api.github.com/users/flutter/events{/privacy}", - "received_events_url": "https://api.github.com/users/flutter/received_events", - "type": "User", - "site_admin": false - }, - "draft" : "$isDraft", - "body": "The body", - "created_at": "2019-07-03T07:14:35Z", - "updated_at": "2019-07-03T16:34:53Z", - "closed_at": null, - "merged_at": "2019-07-03T16:34:53Z", - "merge_commit_sha": "$mergeCommitSha", - "assignee": null, - "assignees": [], - "requested_reviewers": [], - "requested_teams": [], - "labels": [ - { - "id": 487496476, - "node_id": "MDU6TGFiZWw0ODc0OTY0NzY=", - "url": "https://api.github.com/repos/${slug.fullName}/labels/cla:%20yes", - "name": "cla: yes", - "color": "ffffff", - "default": false - }, - { - "id": 284437560, - "node_id": "MDU6TGFiZWwyODQ0Mzc1NjA=", - "url": "https://api.github.com/repos/${slug.fullName}/labels/framework", - "name": "framework", - "color": "207de5", - "default": false - }, - ${includeCqLabel ? ''' - { - "id": 283480100, - "node_id": "MDU6TGFiZWwyODM0ODAxMDA=", - "url": "https://api.github.com/repos/${slug.fullName}/labels/tool", - "color": "5319e7", - "default": false - },''' : ''} - { - "id": 283480100, - "node_id": "MDU6TGFiZWwyODM0ODAxMDA=", - "url": "https://api.github.com/repos/${slug.fullName}/labels/tool", - "name": "tool", - "color": "5319e7", - "default": false - } - ], - "milestone": null, - "commits_url": "https://api.github.com/repos/${slug.fullName}/pulls/$number/commits", - "review_comments_url": "https://api.github.com/repos/${slug.fullName}/pulls/$number/comments", - "review_comment_url": "https://api.github.com/repos/${slug.fullName}/pulls/comments{/number}", - "comments_url": "https://api.github.com/repos/${slug.fullName}/issues/$number/comments", - "statuses_url": "https://api.github.com/repos/${slug.fullName}/statuses/be6ff099a4ee56e152a5fa2f37edd10f79d1269a", - "head": { - "label": "$login:$headRef", - "ref": "$headRef", - "sha": "be6ff099a4ee56e152a5fa2f37edd10f79d1269a", - "user": { - "login": "$login", - "id": 8620741, - "node_id": "MDQ6VXNlcjg2MjA3NDE=", - "avatar_url": "https://avatars3.githubusercontent.com/u/8620741?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/flutter", - "html_url": "https://github.com/flutter", - "followers_url": "https://api.github.com/users/flutter/followers", - "following_url": "https://api.github.com/users/flutter/following{/other_user}", - "gists_url": "https://api.github.com/users/flutter/gists{/gist_id}", - "starred_url": "https://api.github.com/users/flutter/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/flutter/subscriptions", - "organizations_url": "https://api.github.com/users/flutter/orgs", - "repos_url": "https://api.github.com/users/flutter/repos", - "events_url": "https://api.github.com/users/flutter/events{/privacy}", - "received_events_url": "https://api.github.com/users/flutter/received_events", - "type": "User", - "site_admin": false - }, - "repo": { - "id": 131232406, - "node_id": "MDEwOlJlcG9zaXRvcnkxMzEyMzI0MDY=", - "name": "${slug.name}", - "full_name": "${slug.fullName}", - "private": false, - "owner": { - "login": "flutter", - "id": 8620741, - "node_id": "MDQ6VXNlcjg2MjA3NDE=", - "avatar_url": "https://avatars3.githubusercontent.com/u/8620741?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/flutter", - "html_url": "https://github.com/flutter", - "followers_url": "https://api.github.com/users/flutter/followers", - "following_url": "https://api.github.com/users/flutter/following{/other_user}", - "gists_url": "https://api.github.com/users/flutter/gists{/gist_id}", - "starred_url": "https://api.github.com/users/flutter/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/flutter/subscriptions", - "organizations_url": "https://api.github.com/users/flutter/orgs", - "repos_url": "https://api.github.com/users/flutter/repos", - "events_url": "https://api.github.com/users/flutter/events{/privacy}", - "received_events_url": "https://api.github.com/users/flutter/received_events", - "type": "User", - "site_admin": false - }, - "html_url": "https://github.com/${slug.fullName}", - "description": "Flutter makes it easy and fast to build beautiful mobile apps.", - "fork": true, - "url": "https://api.github.com/repos/${slug.fullName}", - "forks_url": "https://api.github.com/repos/${slug.fullName}/forks", - "keys_url": "https://api.github.com/repos/${slug.fullName}/keys{/key_id}", - "collaborators_url": "https://api.github.com/repos/${slug.fullName}/collaborators{/collaborator}", - "teams_url": "https://api.github.com/repos/${slug.fullName}/teams", - "hooks_url": "https://api.github.com/repos/${slug.fullName}/hooks", - "issue_events_url": "https://api.github.com/repos/${slug.fullName}/issues/events{/number}", - "events_url": "https://api.github.com/repos/${slug.fullName}/events", - "assignees_url": "https://api.github.com/repos/${slug.fullName}/assignees{/user}", - "branches_url": "https://api.github.com/repos/${slug.fullName}/branches{/branch}", - "tags_url": "https://api.github.com/repos/${slug.fullName}/tags", - "blobs_url": "https://api.github.com/repos/${slug.fullName}/git/blobs{/sha}", - "git_tags_url": "https://api.github.com/repos/${slug.fullName}/git/tags{/sha}", - "git_refs_url": "https://api.github.com/repos/${slug.fullName}/git/refs{/sha}", - "trees_url": "https://api.github.com/repos/${slug.fullName}/git/trees{/sha}", - "statuses_url": "https://api.github.com/repos/${slug.fullName}/statuses/{sha}", - "languages_url": "https://api.github.com/repos/${slug.fullName}/languages", - "stargazers_url": "https://api.github.com/repos/${slug.fullName}/stargazers", - "contributors_url": "https://api.github.com/repos/${slug.fullName}/contributors", - "subscribers_url": "https://api.github.com/repos/${slug.fullName}/subscribers", - "subscription_url": "https://api.github.com/repos/${slug.fullName}/subscription", - "commits_url": "https://api.github.com/repos/${slug.fullName}/commits{/sha}", - "git_commits_url": "https://api.github.com/repos/${slug.fullName}/git/commits{/sha}", - "comments_url": "https://api.github.com/repos/${slug.fullName}/comments{/number}", - "issue_comment_url": "https://api.github.com/repos/${slug.fullName}/issues/comments{/number}", - "contents_url": "https://api.github.com/repos/${slug.fullName}/contents/{+path}", - "compare_url": "https://api.github.com/repos/${slug.fullName}/compare/{base}...{head}", - "merges_url": "https://api.github.com/repos/${slug.fullName}/merges", - "archive_url": "https://api.github.com/repos/${slug.fullName}/{archive_format}{/ref}", - "downloads_url": "https://api.github.com/repos/${slug.fullName}/downloads", - "issues_url": "https://api.github.com/repos/${slug.fullName}/issues{/number}", - "pulls_url": "https://api.github.com/repos/${slug.fullName}/pulls{/number}", - "milestones_url": "https://api.github.com/repos/${slug.fullName}/milestones{/number}", - "notifications_url": "https://api.github.com/repos/${slug.fullName}/notifications{?since,all,participating}", - "labels_url": "https://api.github.com/repos/${slug.fullName}/labels{/name}", - "releases_url": "https://api.github.com/repos/${slug.fullName}/releases{/id}", - "deployments_url": "https://api.github.com/repos/${slug.fullName}/deployments", - "created_at": "2018-04-27T02:03:08Z", - "updated_at": "2019-06-27T06:56:59Z", - "pushed_at": "2019-07-03T19:40:11Z", - "git_url": "git://github.com/${slug.fullName}.git", - "ssh_url": "git@github.com:${slug.fullName}.git", - "clone_url": "https://github.com/${slug.fullName}.git", - "svn_url": "https://github.com/${slug.fullName}", - "homepage": "https://flutter.io", - "size": 94508, - "stargazers_count": 1, - "watchers_count": 1, - "language": "Dart", - "has_issues": false, - "has_projects": true, - "has_downloads": true, - "has_wiki": true, - "has_pages": false, - "forks_count": 0, - "mirror_url": null, - "archived": false, - "disabled": false, - "open_issues_count": 0, - "license": { - "key": "other", - "name": "Other", - "spdx_id": "NOASSERTION", - "url": null, - "node_id": "MDc6TGljZW5zZTA=" - }, - "forks": 0, - "open_issues": 0, - "watchers": 1, - "default_branch": "$kDefaultBranchName" - } - }, - "base": { - "label": "flutter:$baseRef", - "ref": "$baseRef", - "sha": "$baseSha", - "user": { - "login": "flutter", - "id": 14101776, - "node_id": "MDEyOk9yZ2FuaXphdGlvbjE0MTAxNzc2", - "avatar_url": "https://avatars3.githubblahblahblah", - "gravatar_id": "", - "url": "https://api.github.com/users/flutter", - "html_url": "https://github.com/flutter", - "followers_url": "https://api.github.com/users/flutter/followers", - "following_url": "https://api.github.com/users/flutter/following{/other_user}", - "gists_url": "https://api.github.com/users/flutter/gists{/gist_id}", - "starred_url": "https://api.github.com/users/flutter/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/flutter/subscriptions", - "organizations_url": "https://api.github.com/users/flutter/orgs", - "repos_url": "https://api.github.com/users/flutter/repos", - "events_url": "https://api.github.com/users/flutter/events{/privacy}", - "received_events_url": "https://api.github.com/users/flutter/received_events", - "type": "Organization", - "site_admin": false - }, - "repo": { - "id": 31792824, - "node_id": "MDEwOlJlcG9zaXRvcnkzMTc5MjgyNA==", - "name": "${slug.name}", - "full_name": "${slug.fullName}", - "private": false, - "owner": { - "login": "flutter", - "id": 14101776, - "node_id": "MDEyOk9yZ2FuaXphdGlvbjE0MTAxNzc2", - "avatar_url": "https://avatars3.githubblahblahblah", - "gravatar_id": "", - "url": "https://api.github.com/users/flutter", - "html_url": "https://github.com/flutter", - "followers_url": "https://api.github.com/users/flutter/followers", - "following_url": "https://api.github.com/users/flutter/following{/other_user}", - "gists_url": "https://api.github.com/users/flutter/gists{/gist_id}", - "starred_url": "https://api.github.com/users/flutter/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/flutter/subscriptions", - "organizations_url": "https://api.github.com/users/flutter/orgs", - "repos_url": "https://api.github.com/users/flutter/repos", - "events_url": "https://api.github.com/users/flutter/events{/privacy}", - "received_events_url": "https://api.github.com/users/flutter/received_events", - "type": "Organization", - "site_admin": false - }, - "html_url": "https://github.com/${slug.fullName}", - "description": "Flutter makes it easy and fast to build beautiful mobile apps.", - "fork": false, - "url": "https://api.github.com/repos/${slug.fullName}", - "forks_url": "https://api.github.com/repos/${slug.fullName}/forks", - "keys_url": "https://api.github.com/repos/${slug.fullName}/keys{/key_id}", - "collaborators_url": "https://api.github.com/repos/${slug.fullName}/collaborators{/collaborator}", - "teams_url": "https://api.github.com/repos/${slug.fullName}/teams", - "hooks_url": "https://api.github.com/repos/${slug.fullName}/hooks", - "issue_events_url": "https://api.github.com/repos/${slug.fullName}/issues/events{/number}", - "events_url": "https://api.github.com/repos/${slug.fullName}/events", - "assignees_url": "https://api.github.com/repos/${slug.fullName}/assignees{/user}", - "branches_url": "https://api.github.com/repos/${slug.fullName}/branches{/branch}", - "tags_url": "https://api.github.com/repos/${slug.fullName}/tags", - "blobs_url": "https://api.github.com/repos/${slug.fullName}/git/blobs{/sha}", - "git_tags_url": "https://api.github.com/repos/${slug.fullName}/git/tags{/sha}", - "git_refs_url": "https://api.github.com/repos/${slug.fullName}/git/refs{/sha}", - "trees_url": "https://api.github.com/repos/${slug.fullName}/git/trees{/sha}", - "statuses_url": "https://api.github.com/repos/${slug.fullName}/statuses/{sha}", - "languages_url": "https://api.github.com/repos/${slug.fullName}/languages", - "stargazers_url": "https://api.github.com/repos/${slug.fullName}/stargazers", - "contributors_url": "https://api.github.com/repos/${slug.fullName}/contributors", - "subscribers_url": "https://api.github.com/repos/${slug.fullName}/subscribers", - "subscription_url": "https://api.github.com/repos/${slug.fullName}/subscription", - "commits_url": "https://api.github.com/repos/${slug.fullName}/commits{/sha}", - "git_commits_url": "https://api.github.com/repos/${slug.fullName}/git/commits{/sha}", - "comments_url": "https://api.github.com/repos/${slug.fullName}/comments{/number}", - "issue_comment_url": "https://api.github.com/repos/${slug.fullName}/issues/comments{/number}", - "contents_url": "https://api.github.com/repos/${slug.fullName}/contents/{+path}", - "compare_url": "https://api.github.com/repos/${slug.fullName}/compare/{base}...{head}", - "merges_url": "https://api.github.com/repos/${slug.fullName}/merges", - "archive_url": "https://api.github.com/repos/${slug.fullName}/{archive_format}{/ref}", - "downloads_url": "https://api.github.com/repos/${slug.fullName}/downloads", - "issues_url": "https://api.github.com/repos/${slug.fullName}/issues{/number}", - "pulls_url": "https://api.github.com/repos/${slug.fullName}/pulls{/number}", - "milestones_url": "https://api.github.com/repos/${slug.fullName}/milestones{/number}", - "notifications_url": "https://api.github.com/repos/${slug.fullName}/notifications{?since,all,participating}", - "labels_url": "https://api.github.com/repos/${slug.fullName}/labels{/name}", - "releases_url": "https://api.github.com/repos/${slug.fullName}/releases{/id}", - "deployments_url": "https://api.github.com/repos/${slug.fullName}/deployments", - "created_at": "2015-03-06T22:54:58Z", - "updated_at": "2019-07-04T02:08:44Z", - "pushed_at": "2019-07-04T02:03:04Z", - "git_url": "git://github.com/${slug.fullName}.git", - "ssh_url": "git@github.com:${slug.fullName}.git", - "clone_url": "https://github.com/${slug.fullName}.git", - "svn_url": "https://github.com/${slug.fullName}", - "homepage": "https://flutter.dev", - "size": 65507, - "stargazers_count": 68944, - "watchers_count": 68944, - "language": "Dart", - "has_issues": true, - "has_projects": true, - "has_downloads": true, - "has_wiki": true, - "has_pages": false, - "forks_count": 7987, - "mirror_url": null, - "archived": false, - "disabled": false, - "open_issues_count": 6536, - "license": { - "key": "other", - "name": "Other", - "spdx_id": "NOASSERTION", - "url": null, - "node_id": "MDc6TGljZW5zZTA=" - }, - "forks": 7987, - "open_issues": 6536, - "watchers": 68944, - "default_branch": "$kDefaultBranchName" - } - }, - "_links": { - "self": { - "href": "https://api.github.com/repos/${slug.fullName}/pulls/$number" - }, - "html": { - "href": "https://github.com/${slug.fullName}/pull/$number" - }, - "issue": { - "href": "https://api.github.com/repos/${slug.fullName}/issues/$number" - }, - "comments": { - "href": "https://api.github.com/repos/${slug.fullName}/issues/$number/comments" - }, - "review_comments": { - "href": "https://api.github.com/repos/${slug.fullName}/pulls/$number/comments" - }, - "review_comment": { - "href": "https://api.github.com/repos/${slug.fullName}/pulls/comments{/number}" - }, - "commits": { - "href": "https://api.github.com/repos/${slug.fullName}/pulls/$number/commits" - }, - "statuses": { - "href": "https://api.github.com/repos/${slug.fullName}/statuses/deadbeef" - } - }, - "author_association": "MEMBER", - "draft" : $isDraft, - "merged": $merged, - "mergeable": $isMergeable, - "rebaseable": true, - "mergeable_state": "draft", - "merged_by": null, - "comments": 1, - "review_comments": 0, - "maintainer_can_modify": true, - "commits": 5, - "additions": 55, - "deletions": 36, - "changed_files": 5 - }, - "repository": { - "id": 1868532, - "node_id": "MDEwOlJlcG9zaXRvcnkxODY4NTMwMDI=", - "name": "${slug.name}", - "full_name": "${slug.fullName}", - "private": false, - "owner": { - "login": "flutter", - "id": 21031067, - "node_id": "MDQ6VXNlcjIxMDMxMDY3", - "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/flutter", - "html_url": "https://github.com/flutter", - "followers_url": "https://api.github.com/users/flutter/followers", - "following_url": "https://api.github.com/users/flutter/following{/other_user}", - "gists_url": "https://api.github.com/users/flutter/gists{/gist_id}", - "starred_url": "https://api.github.com/users/flutter/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/flutter/subscriptions", - "organizations_url": "https://api.github.com/users/flutter/orgs", - "repos_url": "https://api.github.com/users/flutter/repos", - "events_url": "https://api.github.com/users/flutter/events{/privacy}", - "received_events_url": "https://api.github.com/users/flutter/received_events", - "type": "User", - "site_admin": false - }, - "html_url": "https://github.com/${slug.fullName}", - "description": null, - "fork": false, - "url": "https://api.github.com/repos/${slug.fullName}", - "forks_url": "https://api.github.com/repos/${slug.fullName}/forks", - "keys_url": "https://api.github.com/repos/${slug.fullName}/keys{/key_id}", - "collaborators_url": "https://api.github.com/repos/${slug.fullName}/collaborators{/collaborator}", - "teams_url": "https://api.github.com/repos/${slug.fullName}/teams", - "hooks_url": "https://api.github.com/repos/${slug.fullName}/hooks", - "issue_events_url": "https://api.github.com/repos/${slug.fullName}/issues/events{/number}", - "events_url": "https://api.github.com/repos/${slug.fullName}/events", - "assignees_url": "https://api.github.com/repos/${slug.fullName}/assignees{/user}", - "branches_url": "https://api.github.com/repos/${slug.fullName}/branches{/branch}", - "tags_url": "https://api.github.com/repos/${slug.fullName}/tags", - "blobs_url": "https://api.github.com/repos/${slug.fullName}/git/blobs{/sha}", - "git_tags_url": "https://api.github.com/repos/${slug.fullName}/git/tags{/sha}", - "git_refs_url": "https://api.github.com/repos/${slug.fullName}/git/refs{/sha}", - "trees_url": "https://api.github.com/repos/${slug.fullName}/git/trees{/sha}", - "statuses_url": "https://api.github.com/repos/${slug.fullName}/statuses/{sha}", - "languages_url": "https://api.github.com/repos/${slug.fullName}/languages", - "stargazers_url": "https://api.github.com/repos/${slug.fullName}/stargazers", - "contributors_url": "https://api.github.com/repos/${slug.fullName}/contributors", - "subscribers_url": "https://api.github.com/repos/${slug.fullName}/subscribers", - "subscription_url": "https://api.github.com/repos/${slug.fullName}/subscription", - "commits_url": "https://api.github.com/repos/${slug.fullName}/commits{/sha}", - "git_commits_url": "https://api.github.com/repos/${slug.fullName}/git/commits{/sha}", - "comments_url": "https://api.github.com/repos/${slug.fullName}/comments{/number}", - "issue_comment_url": "https://api.github.com/repos/${slug.fullName}/issues/comments{/number}", - "contents_url": "https://api.github.com/repos/${slug.fullName}/contents/{+path}", - "compare_url": "https://api.github.com/repos/${slug.fullName}/compare/{base}...{head}", - "merges_url": "https://api.github.com/repos/${slug.fullName}/merges", - "archive_url": "https://api.github.com/repos/${slug.fullName}/{archive_format}{/ref}", - "downloads_url": "https://api.github.com/repos/${slug.fullName}/downloads", - "issues_url": "https://api.github.com/repos/${slug.fullName}/issues{/number}", - "pulls_url": "https://api.github.com/repos/${slug.fullName}/pulls{/number}", - "milestones_url": "https://api.github.com/repos/${slug.fullName}/milestones{/number}", - "notifications_url": "https://api.github.com/repos/${slug.fullName}/notifications{?since,all,participating}", - "labels_url": "https://api.github.com/repos/${slug.fullName}/labels{/name}", - "releases_url": "https://api.github.com/repos/${slug.fullName}/releases{/id}", - "deployments_url": "https://api.github.com/repos/${slug.fullName}/deployments", - "created_at": "2019-05-15T15:19:25Z", - "updated_at": "2019-05-15T15:19:27Z", - "pushed_at": "2019-05-15T15:20:32Z", - "git_url": "git://github.com/${slug.fullName}.git", - "ssh_url": "git@github.com:${slug.fullName}.git", - "clone_url": "https://github.com/${slug.fullName}.git", - "svn_url": "https://github.com/${slug.fullName}", - "homepage": null, - "size": 0, - "stargazers_count": 0, - "watchers_count": 0, - "language": null, - "has_issues": true, - "has_projects": true, - "has_downloads": true, - "has_wiki": true, - "has_pages": true, - "forks_count": 0, - "mirror_url": null, - "archived": false, - "disabled": false, - "open_issues_count": 2, - "license": null, - "forks": 0, - "open_issues": 2, - "watchers": 0, - "default_branch": "$kDefaultBranchName" - }, - "sender": { - "login": "$login", - "id": 21031067, - "node_id": "MDQ6VXNlcjIxMDMxMDY3", - "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/flutter", - "html_url": "https://github.com/flutter", - "followers_url": "https://api.github.com/users/flutter/followers", - "following_url": "https://api.github.com/users/flutter/following{/other_user}", - "gists_url": "https://api.github.com/users/flutter/gists{/gist_id}", - "starred_url": "https://api.github.com/users/flutter/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/flutter/subscriptions", - "organizations_url": "https://api.github.com/users/flutter/orgs", - "repos_url": "https://api.github.com/users/flutter/repos", - "events_url": "https://api.github.com/users/flutter/events{/privacy}", - "received_events_url": "https://api.github.com/users/flutter/received_events", - "type": "User", - "site_admin": false - } -}'''; -} - -PushMessage generateCheckRunEvent({ - String action = 'created', - int numberOfPullRequests = 1, -}) { - String data = '''{ - "action": "$action", - "check_run": { - "id": 128620228, - "node_id": "MDg6Q2hlY2tSdW4xMjg2MjAyMjg=", - "head_sha": "ec26c3e57ca3a959ca5aad62de7213c562f8c821", - "external_id": "", - "url": "https://api.github.com/repos/flutter/flutter/check-runs/128620228", - "html_url": "https://github.com/flutter/flutter/runs/128620228", - "details_url": "https://octocoders.io", - "status": "queued", - "conclusion": null, - "started_at": "2019-05-15T15:21:12Z", - "completed_at": null, - "output": { - "title": null, - "summary": null, - "text": null, - "annotations_count": 0, - "annotations_url": "https://api.github.com/repos/flutter/flutter/check-runs/128620228/annotations" - }, - "name": "Octocoders-linter", - "check_suite": { - "id": 118578147, - "node_id": "MDEwOkNoZWNrU3VpdGUxMTg1NzgxNDc=", - "head_branch": "changes", - "head_sha": "ec26c3e57ca3a959ca5aad62de7213c562f8c821", - "status": "queued", - "conclusion": null, - "url": "https://api.github.com/repos/flutter/flutter/check-suites/118578147", - "before": "6113728f27ae82c7b1a177c8d03f9e96e0adf246", - "after": "ec26c3e57ca3a959ca5aad62de7213c562f8c821", - "pull_requests": [ - { - "url": "https://api.github.com/repos/flutter/flutter/pulls/2", - "id": 279147437, - "number": 2, - "head": { - "ref": "changes", - "sha": "ec26c3e57ca3a959ca5aad62de7213c562f8c821", - "repo": { - "id": 186853002, - "url": "https://api.github.com/repos/flutter/flutter", - "name": "flutter" - } - }, - "base": { - "ref": "master", - "sha": "f95f852bd8fca8fcc58a9a2d6c842781e32a215e", - "repo": { - "id": 186853002, - "url": "https://api.github.com/repos/flutter/flutter", - "name": "flutter" - } - } - } - ], - "app": { - "id": 29310, - "node_id": "MDM6QXBwMjkzMTA=", - "owner": { - "login": "Octocoders", - "id": 38302899, - "node_id": "MDEyOk9yZ2FuaXphdGlvbjM4MzAyODk5", - "avatar_url": "https://avatars1.githubusercontent.com/u/38302899?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/Octocoders", - "html_url": "https://github.com/Octocoders", - "followers_url": "https://api.github.com/users/Octocoders/followers", - "following_url": "https://api.github.com/users/Octocoders/following{/other_user}", - "gists_url": "https://api.github.com/users/Octocoders/gists{/gist_id}", - "starred_url": "https://api.github.com/users/Octocoders/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/Octocoders/subscriptions", - "organizations_url": "https://api.github.com/users/Octocoders/orgs", - "repos_url": "https://api.github.com/users/Octocoders/repos", - "events_url": "https://api.github.com/users/Octocoders/events{/privacy}", - "received_events_url": "https://api.github.com/users/Octocoders/received_events", - "type": "Organization", - "site_admin": false - }, - "name": "octocoders-linter", - "description": "", - "external_url": "https://octocoders.io", - "html_url": "https://github.com/apps/octocoders-linter", - "created_at": "2019-04-19T19:36:24Z", - "updated_at": "2019-04-19T19:36:56Z", - "permissions": { - "administration": "write", - "checks": "write", - "contents": "write", - "deployments": "write", - "issues": "write", - "members": "write", - "metadata": "read", - "organization_administration": "write", - "organization_hooks": "write", - "organization_plan": "read", - "organization_projects": "write", - "organization_user_blocking": "write", - "pages": "write", - "pull_requests": "write", - "repository_hooks": "write", - "repository_projects": "write", - "statuses": "write", - "team_discussions": "write", - "vulnerability_alerts": "read" - }, - "events": [] - }, - "created_at": "2019-05-15T15:20:31Z", - "updated_at": "2019-05-15T15:20:31Z" - }, - "app": { - "id": 29310, - "node_id": "MDM6QXBwMjkzMTA=", - "owner": { - "login": "Octocoders", - "id": 38302899, - "node_id": "MDEyOk9yZ2FuaXphdGlvbjM4MzAyODk5", - "avatar_url": "https://avatars1.githubusercontent.com/u/38302899?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/Octocoders", - "html_url": "https://github.com/Octocoders", - "followers_url": "https://api.github.com/users/Octocoders/followers", - "following_url": "https://api.github.com/users/Octocoders/following{/other_user}", - "gists_url": "https://api.github.com/users/Octocoders/gists{/gist_id}", - "starred_url": "https://api.github.com/users/Octocoders/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/Octocoders/subscriptions", - "organizations_url": "https://api.github.com/users/Octocoders/orgs", - "repos_url": "https://api.github.com/users/Octocoders/repos", - "events_url": "https://api.github.com/users/Octocoders/events{/privacy}", - "received_events_url": "https://api.github.com/users/Octocoders/received_events", - "type": "Organization", - "site_admin": false - }, - "name": "octocoders-linter", - "description": "", - "external_url": "https://octocoders.io", - "html_url": "https://github.com/apps/octocoders-linter", - "created_at": "2019-04-19T19:36:24Z", - "updated_at": "2019-04-19T19:36:56Z", - "permissions": { - "administration": "write", - "checks": "write", - "contents": "write", - "deployments": "write", - "issues": "write", - "members": "write", - "metadata": "read", - "organization_administration": "write", - "organization_hooks": "write", - "organization_plan": "read", - "organization_projects": "write", - "organization_user_blocking": "write", - "pages": "write", - "pull_requests": "write", - "repository_hooks": "write", - "repository_projects": "write", - "statuses": "write", - "team_discussions": "write", - "vulnerability_alerts": "read" - }, - "events": [] - }, - "pull_requests": ['''; - - for (int i = 0; i < numberOfPullRequests; i++) { - data += '''{ - "url": "https://api.github.com/repos/flutter/flutter/pulls/2", - "id": 279147437, - "number": ${i + 2}, - "head": { - "ref": "changes", - "sha": "ec26c3e57ca3a959ca5aad62de7213c562f8c821", - "repo": { - "id": 186853002, - "url": "https://api.github.com/repos/flutter/flutter", - "name": "flutter" - } - }, - "base": { - "ref": "master", - "sha": "f95f852bd8fca8fcc58a9a2d6c842781e32a215e", - "repo": { - "id": 186853002, - "url": "https://api.github.com/repos/flutter/flutter", - "name": "flutter" - } - } - }'''; - if (i < numberOfPullRequests - 1) { - data += ','; - } - } - data += '''], - "deployment": { - "url": "https://api.github.com/repos/flutter/flutter/deployments/326191728", - "id": 326191728, - "node_id": "MDEwOkRlcGxveW1lbnQzMjYxOTE3Mjg=", - "task": "deploy", - "original_environment": "lab", - "environment": "lab", - "description": null, - "created_at": "2021-02-18T08:22:48Z", - "updated_at": "2021-02-18T09:47:16Z", - "statuses_url": "https://api.github.com/repos/flutter/flutter/deployments/326191728/statuses", - "repository_url": "https://api.github.com/repos/flutter/flutter" - } - }, - "repository": { - "id": 186853002, - "node_id": "MDEwOlJlcG9zaXRvcnkxODY4NTMwMDI=", - "name": "flutter", - "full_name": "flutter/flutter", - "private": false, - "owner": { - "login": "flutter", - "id": 21031067, - "node_id": "MDQ6VXNlcjIxMDMxMDY3", - "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/flutter", - "html_url": "https://github.com/flutter", - "followers_url": "https://api.github.com/users/flutter/followers", - "following_url": "https://api.github.com/users/flutter/following{/other_user}", - "gists_url": "https://api.github.com/users/flutter/gists{/gist_id}", - "starred_url": "https://api.github.com/users/flutter/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/flutter/subscriptions", - "organizations_url": "https://api.github.com/users/flutter/orgs", - "repos_url": "https://api.github.com/users/flutter/repos", - "events_url": "https://api.github.com/users/flutter/events{/privacy}", - "received_events_url": "https://api.github.com/users/flutter/received_events", - "type": "User", - "site_admin": false - }, - "html_url": "https://github.com/flutter/flutter", - "description": null, - "fork": false, - "url": "https://api.github.com/repos/flutter/flutter", - "forks_url": "https://api.github.com/repos/flutter/flutter/forks", - "keys_url": "https://api.github.com/repos/flutter/flutter/keys{/key_id}", - "collaborators_url": "https://api.github.com/repos/flutter/flutter/collaborators{/collaborator}", - "teams_url": "https://api.github.com/repos/flutter/flutter/teams", - "hooks_url": "https://api.github.com/repos/flutter/flutter/hooks", - "issue_events_url": "https://api.github.com/repos/flutter/flutter/issues/events{/number}", - "events_url": "https://api.github.com/repos/flutter/flutter/events", - "assignees_url": "https://api.github.com/repos/flutter/flutter/assignees{/user}", - "branches_url": "https://api.github.com/repos/flutter/flutter/branches{/branch}", - "tags_url": "https://api.github.com/repos/flutter/flutter/tags", - "blobs_url": "https://api.github.com/repos/flutter/flutter/git/blobs{/sha}", - "git_tags_url": "https://api.github.com/repos/flutter/flutter/git/tags{/sha}", - "git_refs_url": "https://api.github.com/repos/flutter/flutter/git/refs{/sha}", - "trees_url": "https://api.github.com/repos/flutter/flutter/git/trees{/sha}", - "statuses_url": "https://api.github.com/repos/flutter/flutter/statuses/{sha}", - "languages_url": "https://api.github.com/repos/flutter/flutter/languages", - "stargazers_url": "https://api.github.com/repos/flutter/flutter/stargazers", - "contributors_url": "https://api.github.com/repos/flutter/flutter/contributors", - "subscribers_url": "https://api.github.com/repos/flutter/flutter/subscribers", - "subscription_url": "https://api.github.com/repos/flutter/flutter/subscription", - "commits_url": "https://api.github.com/repos/flutter/flutter/commits{/sha}", - "git_commits_url": "https://api.github.com/repos/flutter/flutter/git/commits{/sha}", - "comments_url": "https://api.github.com/repos/flutter/flutter/comments{/number}", - "issue_comment_url": "https://api.github.com/repos/flutter/flutter/issues/comments{/number}", - "contents_url": "https://api.github.com/repos/flutter/flutter/contents/{+path}", - "compare_url": "https://api.github.com/repos/flutter/flutter/compare/{base}...{head}", - "merges_url": "https://api.github.com/repos/flutter/flutter/merges", - "archive_url": "https://api.github.com/repos/flutter/flutter/{archive_format}{/ref}", - "downloads_url": "https://api.github.com/repos/flutter/flutter/downloads", - "issues_url": "https://api.github.com/repos/flutter/flutter/issues{/number}", - "pulls_url": "https://api.github.com/repos/flutter/flutter/pulls{/number}", - "milestones_url": "https://api.github.com/repos/flutter/flutter/milestones{/number}", - "notifications_url": "https://api.github.com/repos/flutter/flutter/notifications{?since,all,participating}", - "labels_url": "https://api.github.com/repos/flutter/flutter/labels{/name}", - "releases_url": "https://api.github.com/repos/flutter/flutter/releases{/id}", - "deployments_url": "https://api.github.com/repos/flutter/flutter/deployments", - "created_at": "2019-05-15T15:19:25Z", - "updated_at": "2019-05-15T15:21:03Z", - "pushed_at": "2019-05-15T15:20:57Z", - "git_url": "git://github.com/flutter/flutter.git", - "ssh_url": "git@github.com:flutter/flutter.git", - "clone_url": "https://github.com/flutter/flutter.git", - "svn_url": "https://github.com/flutter/flutter", - "homepage": null, - "size": 0, - "stargazers_count": 0, - "watchers_count": 0, - "language": "Ruby", - "has_issues": true, - "has_projects": true, - "has_downloads": true, - "has_wiki": true, - "has_pages": true, - "forks_count": 1, - "mirror_url": null, - "archived": false, - "disabled": false, - "open_issues_count": 2, - "license": null, - "forks": 1, - "open_issues": 2, - "watchers": 0, - "default_branch": "master" - }, - "sender": { - "login": "flutter", - "id": 21031067, - "node_id": "MDQ6VXNlcjIxMDMxMDY3", - "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/flutter", - "html_url": "https://github.com/flutter", - "followers_url": "https://api.github.com/users/flutter/followers", - "following_url": "https://api.github.com/users/flutter/following{/other_user}", - "gists_url": "https://api.github.com/users/flutter/gists{/gist_id}", - "starred_url": "https://api.github.com/users/flutter/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/flutter/subscriptions", - "organizations_url": "https://api.github.com/users/flutter/orgs", - "repos_url": "https://api.github.com/users/flutter/repos", - "events_url": "https://api.github.com/users/flutter/events{/privacy}", - "received_events_url": "https://api.github.com/users/flutter/received_events", - "type": "User", - "site_admin": false - } -}'''; - final pb.GithubWebhookMessage message = pb.GithubWebhookMessage( - event: 'check_run', - payload: data, - ); - return PushMessage( - data: message.writeToJson(), - messageId: 'abc123', - ); -} - -PushMessage generateCreateBranchMessage(String branchName, String repository, {bool forked = false}) { - final CreateEvent createEvent = generateCreateBranchEvent(branchName, repository, forked: forked); - final pb.GithubWebhookMessage message = pb.GithubWebhookMessage( - event: 'create', - payload: jsonEncode(createEvent), - ); - return PushMessage(data: message.writeToJson(), messageId: 'abc123'); -} - -CreateEvent generateCreateBranchEvent(String branchName, String repository, {bool forked = false}) => - CreateEvent.fromJson( - jsonDecode(''' -{ - "ref": "$branchName", - "ref_type": "branch", - "master_branch": "master", - "description": null, - "pusher_type": "user", - "repository": { - "id": 186853002, - "node_id": "MDEwOlJlcG9zaXRvcnkxODY4NTMwMDI=", - "name": "${repository.split('/')[1]}", - "full_name": "$repository", - "private": false, - "owner": { - "login": "${repository.split('/')[0]}", - "id": 21031067, - "node_id": "MDQ6VXNlcjIxMDMxMDY3", - "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/Codertocat", - "html_url": "https://github.com/Codertocat", - "followers_url": "https://api.github.com/users/Codertocat/followers", - "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", - "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", - "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", - "organizations_url": "https://api.github.com/users/Codertocat/orgs", - "repos_url": "https://api.github.com/users/Codertocat/repos", - "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", - "received_events_url": "https://api.github.com/users/Codertocat/received_events", - "type": "User", - "site_admin": false - }, - "html_url": "https://github.com/$repository", - "description": null, - "fork": $forked, - "url": "https://api.github.com/repos/$repository", - "forks_url": "https://api.github.com/repos/$repository/forks", - "keys_url": "https://api.github.com/repos/$repository/keys{/key_id}", - "collaborators_url": "https://api.github.com/repos/$repository/collaborators{/collaborator}", - "teams_url": "https://api.github.com/repos/$repository/teams", - "hooks_url": "https://api.github.com/repos/$repository/hooks", - "issue_events_url": "https://api.github.com/repos/$repository/issues/events{/number}", - "events_url": "https://api.github.com/repos/$repository/events", - "assignees_url": "https://api.github.com/repos/$repository/assignees{/user}", - "branches_url": "https://api.github.com/repos/$repository/branches{/branch}", - "tags_url": "https://api.github.com/repos/$repository/tags", - "blobs_url": "https://api.github.com/repos/$repository/git/blobs{/sha}", - "git_tags_url": "https://api.github.com/repos/$repository/git/tags{/sha}", - "git_refs_url": "https://api.github.com/repos/$repository/git/refs{/sha}", - "trees_url": "https://api.github.com/repos/$repository/git/trees{/sha}", - "statuses_url": "https://api.github.com/repos/$repository/statuses/{sha}", - "languages_url": "https://api.github.com/repos/$repository/languages", - "stargazers_url": "https://api.github.com/repos/$repository/stargazers", - "contributors_url": "https://api.github.com/repos/$repository/contributors", - "subscribers_url": "https://api.github.com/repos/$repository/subscribers", - "subscription_url": "https://api.github.com/repos/$repository/subscription", - "commits_url": "https://api.github.com/repos/$repository/commits{/sha}", - "git_commits_url": "https://api.github.com/repos/$repository/git/commits{/sha}", - "comments_url": "https://api.github.com/repos/$repository/comments{/number}", - "issue_comment_url": "https://api.github.com/repos/$repository/issues/comments{/number}", - "contents_url": "https://api.github.com/repos/$repository/contents/{+path}", - "compare_url": "https://api.github.com/repos/$repository/compare/{base}...{head}", - "merges_url": "https://api.github.com/repos/$repository/merges", - "archive_url": "https://api.github.com/repos/$repository/{archive_format}{/ref}", - "downloads_url": "https://api.github.com/repos/$repository/downloads", - "issues_url": "https://api.github.com/repos/$repository/issues{/number}", - "pulls_url": "https://api.github.com/repos/$repository/pulls{/number}", - "milestones_url": "https://api.github.com/repos/$repository/milestones{/number}", - "notifications_url": "https://api.github.com/repos/$repository/notifications{?since,all,participating}", - "labels_url": "https://api.github.com/repos/$repository/labels{/name}", - "releases_url": "https://api.github.com/repos/$repository/releases{/id}", - "deployments_url": "https://api.github.com/repos/$repository/deployments", - "created_at": "2019-05-15T15:19:25Z", - "updated_at": "2019-05-15T15:20:41Z", - "pushed_at": "2019-05-15T15:20:56Z", - "git_url": "git://github.com/$repository.git", - "ssh_url": "git@github.com:Codertocat/Hello-World.git", - "clone_url": "https://github.com/$repository.git", - "svn_url": "https://github.com/$repository", - "homepage": null, - "size": 0, - "stargazers_count": 0, - "watchers_count": 0, - "language": "Ruby", - "has_issues": true, - "has_projects": true, - "has_downloads": true, - "has_wiki": true, - "has_pages": true, - "forks_count": 1, - "mirror_url": null, - "archived": false, - "disabled": false, - "open_issues_count": 2, - "license": null, - "forks": 1, - "open_issues": 2, - "watchers": 0, - "default_branch": "master" - }, - "sender": { - "login": "Codertocat", - "id": 21031067, - "node_id": "MDQ6VXNlcjIxMDMxMDY3", - "avatar_url": "https://avatars1.githubusercontent.com/u/21031067?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/Codertocat", - "html_url": "https://github.com/Codertocat", - "followers_url": "https://api.github.com/users/Codertocat/followers", - "following_url": "https://api.github.com/users/Codertocat/following{/other_user}", - "gists_url": "https://api.github.com/users/Codertocat/gists{/gist_id}", - "starred_url": "https://api.github.com/users/Codertocat/starred{/owner}{/repo}", - "subscriptions_url": "https://api.github.com/users/Codertocat/subscriptions", - "organizations_url": "https://api.github.com/users/Codertocat/orgs", - "repos_url": "https://api.github.com/users/Codertocat/repos", - "events_url": "https://api.github.com/users/Codertocat/events{/privacy}", - "received_events_url": "https://api.github.com/users/Codertocat/received_events", - "type": "User", - "site_admin": false - } -}''') as Map, - ); - -PushMessage generatePushMessage(String branch, String organization, String repository) { - final Map event = generatePushEvent(branch, organization, repository); - final pb.GithubWebhookMessage message = pb.GithubWebhookMessage(event: 'push', payload: jsonEncode(event)); - return PushMessage(data: message.writeToJson(), messageId: 'abc123'); -} - -Map generatePushEvent( - String branch, - String organization, - String repository, { - String sha = 'def456def456def456', - String message = 'Commit-message', - String avatarUrl = 'https://fakegithubcontent.com/google_profile', - String username = 'googledotcom', -}) => - jsonDecode(''' -{ - "ref": "refs/heads/$branch", - "before": "abc123abc123abc123", - "after": "$sha", - "sender": { - "login": "$username", - "avatar_url": "$avatarUrl" - }, - "commits": [ - { - "id": "ba2f6608108d174c4a6e6e093a4ddcf313656748", - "message": "Adding null safety", - "timestamp": "2023-09-05T15:01:04-05:00", - "url": "https://github.com/org/repo/commit/abc123abc123abc123" - } - ], - "head_commit": { - "id": "$sha", - "message": "$message", - "timestamp": "2023-09-05T15:01:04-05:00", - "url": "https://github.com/org/repo/commit/abc123abc123abc123" - }, - "repository": { - "name": "$repository", - "full_name": "$organization/$repository" - } -} -'''); diff --git a/app_dart/tool/build.sh b/app_dart/tool/build.sh deleted file mode 100755 index b253343d9..000000000 --- a/app_dart/tool/build.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env bash -# 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. - -# Fetches corresponding dart sdk from CIPD for different platforms, builds -# an executable binary of bin/generate_jspb.dart to `build` folder. -# -# This only supports Linux, but can be generalized for Windows and Mac. - -set -e - -command -v cipd > /dev/null || { - echo "Please install CIPD (available from depot_tools) and add to path first."; - exit -1; -} - -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd)" - -cipd ensure -ensure-file $DIR/ensure_file -root $DIR - -pushd $DIR/.. - -if [[ -d "build" ]]; then - echo "Please remove the build directory before proceeding" - exit -1 -fi - -mkdir -p build -tool/dart-sdk/bin/dart pub get -tool/dart-sdk/bin/dart compile exe bin/generate_jspb.dart -o build/ci_yaml_jspb - -# Definitions related to uploading packages to CIPD -echo "# This file was auto-generated by Cocoon -# For more information, see https://github.com/flutter/cocoon -# -# This file defines information related to CIPD for distribution of generate_jspb. -package: flutter/ci_yaml/generate_jspb/linux-amd64 -description: Binary to convert a ci.yaml to a JSON proto, which is readable by flutter.googlesource.com/infra. -data: - - file: ci_yaml_jspb -" > build/cipd.yaml - -popd diff --git a/app_dart/tool/ensure_file b/app_dart/tool/ensure_file deleted file mode 100644 index b537e1f4a..000000000 --- a/app_dart/tool/ensure_file +++ /dev/null @@ -1,3 +0,0 @@ -$ServiceURL https://chrome-infra-packages.appspot.com/ - -dart/dart-sdk/${os}-${arch} stable diff --git a/auto_submit/.dockerignore b/auto_submit/.dockerignore deleted file mode 100644 index 21504f8fe..000000000 --- a/auto_submit/.dockerignore +++ /dev/null @@ -1,9 +0,0 @@ -.dockerignore -Dockerfile -build/ -.dart_tool/ -.git/ -.github/ -.gitignore -.idea/ -.packages diff --git a/auto_submit/.gitignore b/auto_submit/.gitignore deleted file mode 100644 index 3c8a15727..000000000 --- a/auto_submit/.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/auto_submit/CHANGELOG.md b/auto_submit/CHANGELOG.md deleted file mode 100644 index 8d73078cb..000000000 --- a/auto_submit/CHANGELOG.md +++ /dev/null @@ -1,3 +0,0 @@ -## 0.0.1 - -- Initial checkin of basic server diff --git a/auto_submit/Dockerfile b/auto_submit/Dockerfile deleted file mode 100644 index 27652a3d4..000000000 --- a/auto_submit/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/auto_submit/LICENSE b/auto_submit/LICENSE deleted file mode 100644 index d5384ca42..000000000 --- a/auto_submit/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/auto_submit/README.md b/auto_submit/README.md deleted file mode 100644 index 59ebc1e44..000000000 --- a/auto_submit/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Autosubmit - -RFC is available in https://flutter.dev/go/autosubmit diff --git a/auto_submit/analysis_options.yaml b/auto_submit/analysis_options.yaml deleted file mode 100644 index a200ec83b..000000000 --- a/auto_submit/analysis_options.yaml +++ /dev/null @@ -1,9 +0,0 @@ -include: ../analysis_options.yaml - -analyzer: - language: - strict-raw-types: false # TODO: Remove this lint - -linter: - rules: - constant_identifier_names: false # we have all capitalized enums in check_for_waiting_pull_requests_test.dart \ No newline at end of file diff --git a/auto_submit/app.yaml b/auto_submit/app.yaml deleted file mode 100644 index 8a10f40b6..000000000 --- a/auto_submit/app.yaml +++ /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. - -runtime: custom -env: flex -service: auto-submit - -resources: - memory_gb: 2.0 - -readiness_check: - path: "/readiness_check" - check_interval_sec: 20 - timeout_sec: 20 - failure_threshold: 10 - success_threshold: 2 - app_start_timeout_sec: 300 diff --git a/auto_submit/bin/server.dart b/auto_submit/bin/server.dart deleted file mode 100644 index df9595de6..000000000 --- a/auto_submit/bin/server.dart +++ /dev/null @@ -1,62 +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. - -import 'dart:async'; - -import 'package:appengine/appengine.dart'; -import 'package:auto_submit/helpers.dart'; -import 'package:auto_submit/request_handling/authentication.dart'; -import 'package:auto_submit/requests/check_pull_request.dart'; -import 'package:auto_submit/requests/check_revert_request.dart'; -import 'package:auto_submit/requests/github_webhook.dart'; -import 'package:auto_submit/requests/readiness_check.dart'; -import 'package:auto_submit/service/config.dart'; -import 'package:auto_submit/service/secrets.dart'; -import 'package:neat_cache/neat_cache.dart'; -import 'package:shelf_router/shelf_router.dart'; - -/// Number of entries allowed in [Cache]. -const int kCacheSize = 1024; - -Future main() async { - await withAppEngineServices(() async { - useLoggingPackageAdaptor(); - - final cache = Cache.inMemoryCacheProvider(kCacheSize); - final Config config = Config( - cacheProvider: cache, - secretManager: CloudSecretManager(), - ); - const CronAuthProvider authProvider = CronAuthProvider(); - - final Router router = Router() - ..post( - '/webhook', - GithubWebhook( - config: config, - ).post, - ) - ..get( - '/check-pull-request', - CheckPullRequest( - config: config, - cronAuthProvider: authProvider, - ).run, - ) - ..get( - '/check-revert-requests', - CheckRevertRequest( - config: config, - cronAuthProvider: authProvider, - ).run, - ) - ..get( - '/readiness_check', - ReadinessCheck( - config: config, - ).run, - ); - await serveHandler(router.call); - }); -} diff --git a/auto_submit/build.yaml b/auto_submit/build.yaml deleted file mode 100644 index fdbbd17df..000000000 --- a/auto_submit/build.yaml +++ /dev/null @@ -1,8 +0,0 @@ -targets: - $default: - builders: - json_serializable: - options: - # Options configure how source code is generated for every - # `@JsonSerializable`-annotated class in the package. - field_rename: snake diff --git a/auto_submit/cloudbuild_auto_submit.yaml b/auto_submit/cloudbuild_auto_submit.yaml deleted file mode 100644 index 2b65de82f..000000000 --- a/auto_submit/cloudbuild_auto_submit.yaml +++ /dev/null @@ -1,32 +0,0 @@ -# Provide instructions for google Cloud Build to auto-build flutter -# auto-submit bot 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 docker image - - name: 'us-docker.pkg.dev/cloud-builders/ga/v1/docker' - args: ['build', '-t', 'us-docker.pkg.dev/$PROJECT_ID/appengine/auto-submit.version-$SHORT_SHA', 'auto_submit'] - - # Trigger the cloud build that deploys the docker image - - name: gcr.io/cloud-builders/gcloud - entrypoint: '/bin/bash' - args: - - '-c' - - |- - gcloud builds submit \ - --config auto_submit/cloudbuild_auto_submit_deploy.yaml \ - --substitutions="SHORT_SHA=$SHORT_SHA" \ - --async - -timeout: 1200s - -images: ['us-docker.pkg.dev/$PROJECT_ID/appengine/auto-submit.version-$SHORT_SHA'] - -# If build provenance is not generated, the docker deployment will fail. -options: - requestedVerifyOption: VERIFIED diff --git a/auto_submit/cloudbuild_auto_submit_deploy.yaml b/auto_submit/cloudbuild_auto_submit_deploy.yaml deleted file mode 100644 index 968990a47..000000000 --- a/auto_submit/cloudbuild_auto_submit_deploy.yaml +++ /dev/null @@ -1,41 +0,0 @@ -# Provide instructions for google Cloud Build to auto-build flutter -# auto submit 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/auto-submit.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_auto_submit.sh $PROJECT_ID $SHORT_SHA - fi - -timeout: 1200s diff --git a/auto_submit/lib/action/git_cli_revert_method.dart b/auto_submit/lib/action/git_cli_revert_method.dart deleted file mode 100644 index 7ee107353..000000000 --- a/auto_submit/lib/action/git_cli_revert_method.dart +++ /dev/null @@ -1,93 +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:io'; - -import 'package:auto_submit/action/revert_method.dart'; -import 'package:auto_submit/configuration/repository_configuration.dart'; -import 'package:auto_submit/git/cli_command.dart'; -import 'package:auto_submit/git/utilities.dart'; -import 'package:auto_submit/git/git_cli.dart'; -import 'package:auto_submit/git/git_repository_manager.dart'; -import 'package:auto_submit/requests/exceptions.dart'; -import 'package:auto_submit/service/config.dart'; -import 'package:auto_submit/service/github_service.dart'; -import 'package:auto_submit/service/log.dart'; -import 'package:auto_submit/service/revert_issue_body_formatter.dart'; -import 'package:github/github.dart' as github; -import 'package:github/github.dart'; -import 'package:retry/retry.dart'; - -class GitCliRevertMethod implements RevertMethod { - @override - Future createRevert( - Config config, - String initiatingAuthor, - github.PullRequest pullRequest, - ) async { - final github.RepositorySlug slug = pullRequest.base!.repo!.slug(); - final String commitSha = pullRequest.mergeCommitSha!; - // we will need to collect the pr number after the revert request is generated. - - final RepositoryConfiguration repositoryConfiguration = await config.getRepositoryConfiguration(slug); - final String baseBranch = repositoryConfiguration.defaultBranch; - - final String cloneToDirectory = '${slug.name}_$commitSha'; - final GitRepositoryManager gitRepositoryManager = GitRepositoryManager( - slug: slug, - workingDirectory: Directory.current.path, - cloneToDirectory: cloneToDirectory, - gitCli: GitCli(GitAccessMethod.HTTP, CliCommand()), - ); - - // The exception is caught by the thrower. - try { - await gitRepositoryManager.cloneRepository(); - await gitRepositoryManager.setupConfig(); - await gitRepositoryManager.revertCommit(baseBranch, commitSha, slug, await config.generateGithubToken(slug)); - } finally { - await gitRepositoryManager.deleteRepository(); - } - - final GitRevertBranchName gitRevertBranchName = GitRevertBranchName(commitSha); - final GithubService githubService = await config.createGithubService(slug); - - const RetryOptions retryOptions = - RetryOptions(delayFactor: Duration(seconds: 1), maxDelay: Duration(seconds: 1), maxAttempts: 4); - - Branch? branch; - // Attempt a few times to get the branch name. This may not be needed. - // Let the exception bubble up from here. - await retryOptions.retry( - () async { - branch = await githubService.getBranch(slug, gitRevertBranchName.branch); - }, - retryIf: (Exception e) => e is NotFoundException, - ); - - log.info('found branch ${slug.fullName}/${branch!.name}, safe to create revert request of ${pullRequest.number!}.'); - - final RevertIssueBodyFormatter formatter = RevertIssueBodyFormatter( - slug: slug, - originalPrNumber: pullRequest.number!, - initiatingAuthor: initiatingAuthor, - originalPrTitle: pullRequest.title, - originalPrBody: pullRequest.body, - ).format; - - log.info('Attempting to create pull request with ${slug.fullName}/${gitRevertBranchName.branch}.'); - final github.PullRequest revertPullRequest = await githubService.createPullRequest( - slug: slug, - title: formatter.revertPrTitle, - head: gitRevertBranchName.branch, - base: baseBranch, - draft: false, - body: formatter.revertPrBody, - ); - - log.info('pull request number is: ${slug.fullName}/${revertPullRequest.number}'); - - return revertPullRequest; - } -} diff --git a/auto_submit/lib/action/revert_method.dart b/auto_submit/lib/action/revert_method.dart deleted file mode 100644 index 22d58c645..000000000 --- a/auto_submit/lib/action/revert_method.dart +++ /dev/null @@ -1,11 +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:auto_submit/service/config.dart'; -import 'package:github/github.dart' as github; - -abstract class RevertMethod { - // Allows substitution of the method of creating the revert request. - Future createRevert(Config config, String initiatingAuthor, github.PullRequest pullRequest); -} diff --git a/auto_submit/lib/configuration/repository_configuration.dart b/auto_submit/lib/configuration/repository_configuration.dart deleted file mode 100644 index 7eea7d15a..000000000 --- a/auto_submit/lib/configuration/repository_configuration.dart +++ /dev/null @@ -1,126 +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:auto_submit/exception/configuration_exception.dart'; -import 'package:yaml/yaml.dart'; - -/// The RepositoryConfiguration stores the pertinent information that autosubmit -/// will need when submiting and validating pull requests for a particular -/// repository. -class RepositoryConfiguration { - // Autosubmit configuration keys as found in the both the global and local - // yaml configuraiton files. - static const String allowConfigOverrideKey = 'allow_config_override'; - static const String defaultBranchKey = 'default_branch'; - static const String autoApprovalAccountsKey = 'auto_approval_accounts'; - static const String approvingReviewsKey = 'approving_reviews'; - static const String approvalGroupKey = 'approval_group'; - static const String runCiKey = 'run_ci'; - static const String supportNoReviewRevertKey = 'support_no_review_revert'; - static const String requiredCheckRunsOnRevertKey = 'required_checkruns_on_revert'; - - static const String defaultBranchStr = 'default'; - - RepositoryConfiguration({ - allowConfigOverride, - defaultBranch, - autoApprovalAccounts, - approvingReviews, - approvalGroup, - runCi, - supportNoReviewReverts, - requiredCheckRunsOnRevert, - }) : allowConfigOverride = allowConfigOverride ?? false, - defaultBranch = defaultBranch ?? defaultBranchStr, - autoApprovalAccounts = autoApprovalAccounts ?? {}, - approvingReviews = approvingReviews ?? 2, - approvalGroup = approvalGroup ?? 'flutter-hackers', - runCi = runCi ?? true, - supportNoReviewReverts = supportNoReviewReverts ?? true, - requiredCheckRunsOnRevert = requiredCheckRunsOnRevert ?? {}; - - /// This flag allows the repository to override the org level configuration. - bool allowConfigOverride; - - /// The default branch that pull requests will be merged into. - String defaultBranch; - - /// The accounts that have auto approval on their pull requests. - Set autoApprovalAccounts; - - /// The number of reviews needed for a pull request. If the reviewer is part - /// of the approval group they will need ([approvingReviews] - 1) number of - /// reviews in order to merge the pull request, if they are not part of the - /// approval group the will need [approvingReviews] number of reviews. - int approvingReviews; - - /// The group that the pull request author will need pull requests from. - String approvalGroup; - - /// Flag to determine whether or not to wait for all the ci checks to finish - /// before allowing a merge of the pull request. - bool runCi; - - /// Flag that determines if reverts are allowed without a review. - bool supportNoReviewReverts; - - /// Set of checkruns that must complete before a revert pull request can be - /// merged. - Set requiredCheckRunsOnRevert; - - @override - String toString() { - final StringBuffer stringBuffer = StringBuffer(); - stringBuffer.writeln('$allowConfigOverrideKey: $allowConfigOverride'); - stringBuffer.writeln('$defaultBranchKey: $defaultBranch'); - stringBuffer.writeln('$autoApprovalAccountsKey:'); - for (String account in autoApprovalAccounts) { - stringBuffer.writeln(' - $account'); - } - stringBuffer.writeln('$approvingReviewsKey: $approvingReviews'); - stringBuffer.writeln('$approvalGroupKey: $approvalGroup'); - stringBuffer.writeln('$runCiKey: $runCi'); - stringBuffer.writeln('$supportNoReviewRevertKey: $supportNoReviewReverts'); - stringBuffer.writeln('$requiredCheckRunsOnRevertKey:'); - for (String checkrun in requiredCheckRunsOnRevert) { - stringBuffer.writeln(' - $checkrun'); - } - return stringBuffer.toString(); - } - - static RepositoryConfiguration fromYaml(String yaml) { - final dynamic yamlDoc = loadYaml(yaml); - - final Set autoApprovalAccounts = {}; - final YamlList? yamlAutoApprovalAccounts = yamlDoc[autoApprovalAccountsKey]; - if (yamlAutoApprovalAccounts != null) { - for (YamlNode element in yamlAutoApprovalAccounts.nodes) { - autoApprovalAccounts.add(element.value as String); - } - } - - if (yamlDoc[approvalGroupKey] == null) { - throw ConfigurationException('The approval group is a required field.'); - } - - final Set requiredCheckRunsOnRevert = {}; - final YamlList? yamlRequiredCheckRuns = yamlDoc[requiredCheckRunsOnRevertKey]; - if (yamlRequiredCheckRuns != null) { - for (YamlNode element in yamlRequiredCheckRuns.nodes) { - requiredCheckRunsOnRevert.add(element.value as String); - } - } - - return RepositoryConfiguration( - allowConfigOverride: yamlDoc[allowConfigOverrideKey], - defaultBranch: yamlDoc[defaultBranchKey], - autoApprovalAccounts: autoApprovalAccounts, - approvingReviews: yamlDoc[approvingReviewsKey], - approvalGroup: yamlDoc[approvalGroupKey], - runCi: yamlDoc[runCiKey], - supportNoReviewReverts: yamlDoc[supportNoReviewRevertKey], - requiredCheckRunsOnRevert: requiredCheckRunsOnRevert, - ); - } -} diff --git a/auto_submit/lib/configuration/repository_configuration_manager.dart b/auto_submit/lib/configuration/repository_configuration_manager.dart deleted file mode 100644 index 56f59e00a..000000000 --- a/auto_submit/lib/configuration/repository_configuration_manager.dart +++ /dev/null @@ -1,167 +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:auto_submit/configuration/repository_configuration.dart'; -import 'package:auto_submit/service/config.dart'; -import 'package:auto_submit/service/github_service.dart'; -import 'package:auto_submit/service/log.dart'; -import 'package:github/github.dart'; -import 'package:mutex/mutex.dart'; -import 'package:neat_cache/neat_cache.dart'; - -/// The [RepositoryConfigurationManager] is responsible for fetching and merging -/// the autosubmit configuration from the Org level repository and if needed -/// fetching the override configuration from the pull request repository. -/// -/// It will attempt to access the cache first before repulling the configuraiton -/// from the repositories. This is currently set at a 10 minute TTL. -class RepositoryConfigurationManager { - RepositoryConfigurationManager(this.config, this.cache); - - // Mutex protects the calls to cache while the [RepositoryConfiguration] is - // collected from github. - final Mutex _mutex = Mutex(); - - static const String fileSeparator = '/'; - // This is the well named organization level repository and configuration file - // we will read before looking to see if there is a local file with - // overwrites. - static const String orgRepository = '.github'; - static const String dirName = 'autosubmit'; - static const String fileName = 'autosubmit.yml'; - - final Config config; - final Cache cache; - - /// Read the configuration from the cache given the slug, if the config is not - /// in the cache then go and get it from the repository and store it in the - /// cache. - Future readRepositoryConfiguration( - RepositorySlug slug, - ) async { - await _mutex.acquire(); - try { - // Get the contents from the cache or go to github. - final cacheValue = await cache['${slug.fullName}$fileSeparator$fileName'].get( - () async => _getConfiguration(slug), - config.repositoryConfigurationTtl, - ); - final String cacheYaml = String.fromCharCodes(cacheValue); - log.info('Converting yaml to RepositoryConfiguration: $cacheYaml'); - return RepositoryConfiguration.fromYaml(cacheYaml); - } finally { - _mutex.release(); - } - } - - /// Collect the configuration from github and handle the cache conversion to - /// bytes. - Future> _getConfiguration( - RepositorySlug slug, - ) async { - // Read the org level configuraiton file first. - log.info('Getting org level configuration.'); - // Get the Org level configuration. - final RepositorySlug orgSlug = RepositorySlug(slug.owner, orgRepository); - GithubService githubService = await config.createGithubService(orgSlug); - final String orgLevelConfig = await githubService.getFileContents(orgSlug, '$dirName$fileSeparator$fileName'); - final RepositoryConfiguration globalRepositoryConfiguration = RepositoryConfiguration.fromYaml(orgLevelConfig); - - // Collect the default branch if it was not supplied. - if (globalRepositoryConfiguration.defaultBranch == RepositoryConfiguration.defaultBranchStr) { - globalRepositoryConfiguration.defaultBranch = await githubService.getDefaultBranch(slug); - } - log.info('Default branch was found to be ${globalRepositoryConfiguration.defaultBranch} for ${slug.fullName}.'); - - // If the override flag is set to true we check the pull request's - // repository to collect any values that will override the global config. - if (globalRepositoryConfiguration.allowConfigOverride) { - log.info('Override is set, collecting and merging local repository configuration.'); - githubService = await config.createGithubService(slug); - - String? localRepositoryConfigurationYaml; - try { - localRepositoryConfigurationYaml = await githubService.getFileContents(slug, '$dirName$fileSeparator$fileName'); - final RepositoryConfiguration localRepositoryConfiguration = - RepositoryConfiguration.fromYaml(localRepositoryConfigurationYaml); - final RepositoryConfiguration mergedRepositoryConfiguration = mergeConfigurations( - globalRepositoryConfiguration, - localRepositoryConfiguration, - ); - return mergedRepositoryConfiguration.toString().codeUnits; - } on GitHubError { - log.warning( - 'Configuration override was set but no local repository configuration file was found in ${slug.fullName}, using global configuration.', - ); - } - } - - return globalRepositoryConfiguration.toString().codeUnits; - } - - /// Merge the local [RepositoryConfiguration] with the global - /// [RepositoryConfiguration]. - /// - /// Values that are lists are additive. Values that are not lists overwrite - /// the value in the global configuration. - /// - /// The number of approving reviews in the local configuration cannot override - /// the global configuration if it is a lower value. - /// - /// We also do not need to allow the default branch override as it is - /// collected from the repository directly. - RepositoryConfiguration mergeConfigurations( - RepositoryConfiguration globalConfiguration, - RepositoryConfiguration localConfiguration, - ) { - final RepositoryConfiguration mergedRepositoryConfiguration = RepositoryConfiguration( - allowConfigOverride: globalConfiguration.allowConfigOverride, - defaultBranch: globalConfiguration.defaultBranch, - autoApprovalAccounts: globalConfiguration.autoApprovalAccounts, - approvingReviews: globalConfiguration.approvingReviews, - approvalGroup: globalConfiguration.approvalGroup, - runCi: globalConfiguration.runCi, - supportNoReviewReverts: globalConfiguration.supportNoReviewReverts, - requiredCheckRunsOnRevert: globalConfiguration.requiredCheckRunsOnRevert, - ); - - // auto approval accounts, they should be empty if nothing was defined - if (localConfiguration.autoApprovalAccounts.isNotEmpty) { - mergedRepositoryConfiguration.autoApprovalAccounts.addAll(localConfiguration.autoApprovalAccounts); - } - - // approving reviews - // this may not be set lower than the global configuration value - final int localApprovingReviews = localConfiguration.approvingReviews; - if (localApprovingReviews > globalConfiguration.approvingReviews) { - mergedRepositoryConfiguration.approvingReviews = localApprovingReviews; - } - - // approval group - final String localApprovalGroup = localConfiguration.approvalGroup; - if (localApprovalGroup.isNotEmpty) { - mergedRepositoryConfiguration.approvalGroup = localApprovalGroup; - } - - // run ci - // validates the checks runs - final bool localRunCi = localConfiguration.runCi; - if (globalConfiguration.runCi != localRunCi) { - mergedRepositoryConfiguration.runCi = localRunCi; - } - - // support no revert reviews - this will be a moot point after revert is updated - final bool localSupportNoReviewReverts = localConfiguration.supportNoReviewReverts; - if (localSupportNoReviewReverts != globalConfiguration.supportNoReviewReverts) { - mergedRepositoryConfiguration.supportNoReviewReverts = localSupportNoReviewReverts; - } - - // required checkruns on revert, they should be empty if nothing was defined - if (localConfiguration.requiredCheckRunsOnRevert.isNotEmpty) { - mergedRepositoryConfiguration.requiredCheckRunsOnRevert.addAll(localConfiguration.requiredCheckRunsOnRevert); - } - - return mergedRepositoryConfiguration; - } -} diff --git a/auto_submit/lib/exception/bigquery_exception.dart b/auto_submit/lib/exception/bigquery_exception.dart deleted file mode 100644 index 2c79f63ff..000000000 --- a/auto_submit/lib/exception/bigquery_exception.dart +++ /dev/null @@ -1,13 +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. - -class BigQueryException implements Exception { - /// Create a custom exception for Big Query Errors. - BigQueryException(this.cause); - - final String cause; - - @override - String toString() => cause; -} diff --git a/auto_submit/lib/exception/configuration_exception.dart b/auto_submit/lib/exception/configuration_exception.dart deleted file mode 100644 index 00c7d592e..000000000 --- a/auto_submit/lib/exception/configuration_exception.dart +++ /dev/null @@ -1,13 +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. - -class ConfigurationException implements Exception { - /// Create a custom exception for Autosubmit Configuration Errors. - ConfigurationException(this.cause); - - final String cause; - - @override - String toString() => cause; -} diff --git a/auto_submit/lib/exception/retryable_exception.dart b/auto_submit/lib/exception/retryable_exception.dart deleted file mode 100644 index ba7c3a030..000000000 --- a/auto_submit/lib/exception/retryable_exception.dart +++ /dev/null @@ -1,13 +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. - -/// General exception for retryable error catching. -class RetryableException implements Exception { - const RetryableException(this.cause); - - final String cause; - - @override - String toString() => cause; -} diff --git a/auto_submit/lib/foundation/providers.dart b/auto_submit/lib/foundation/providers.dart deleted file mode 100644 index 78893e743..000000000 --- a/auto_submit/lib/foundation/providers.dart +++ /dev/null @@ -1,16 +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. - -import 'package:http/http.dart' as http; - -/// Signature for a function that returns an [http.Client]. -typedef HttpProvider = http.Client Function(); - -/// Class that holds static default providers. -class Providers { - const Providers._(); - - /// Creates a [http.Client] that interacts with the internet. - static http.Client freshHttpClient() => http.Client(); -} diff --git a/auto_submit/lib/foundation/typedefs.dart b/auto_submit/lib/foundation/typedefs.dart deleted file mode 100644 index 7573512d0..000000000 --- a/auto_submit/lib/foundation/typedefs.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:http/http.dart' as http; - -/// Signature for a function that returns an [HttpClient]. -/// -/// This is used by [CronAuthProvider] 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/auto_submit/lib/git/cli_command.dart b/auto_submit/lib/git/cli_command.dart deleted file mode 100644 index 5d6e7f100..000000000 --- a/auto_submit/lib/git/cli_command.dart +++ /dev/null @@ -1,58 +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:io'; - -class CliCommand { - CliCommand(); - - /// Method runs a single command in a shell. - Future runCliCommand({ - required String executable, - required List arguments, - bool throwOnError = true, - String? workingDirectory, - }) async { - final process = await Process.start( - executable, - arguments, - workingDirectory: workingDirectory, - runInShell: true, - mode: ProcessStartMode.normal, - ); - - final result = await Future.wait([ - process.exitCode, - process.stdout.transform(const SystemEncoding().decoder).join(), - process.stderr.transform(const SystemEncoding().decoder).join(), - ]); - - final ProcessResult processResult = ProcessResult( - process.pid, - result[0] as int, - result[1] as String, - result[2] as String, - ); - - if (throwOnError) { - if (processResult.exitCode != 0) { - final Map outputs = { - if (processResult.stdout != null) 'Standard out': processResult.stdout.toString().trim(), - if (processResult.stderr != null) 'Standard error': processResult.stderr.toString().trim(), - }..removeWhere((k, v) => v.isEmpty); - - String errorMessage; - if (outputs.isEmpty) { - errorMessage = 'Unknown error.'; - } else { - errorMessage = outputs.entries.map((entry) => '${entry.key}\n${entry.value}').join('\n'); - } - - throw ProcessException(executable, arguments, errorMessage, processResult.exitCode); - } - } - - return processResult; - } -} diff --git a/auto_submit/lib/git/git_cli.dart b/auto_submit/lib/git/git_cli.dart deleted file mode 100644 index 66caa978e..000000000 --- a/auto_submit/lib/git/git_cli.dart +++ /dev/null @@ -1,335 +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:io'; -import 'package:github/github.dart'; -import 'package:logging/logging.dart'; -import 'cli_command.dart'; -import 'utilities.dart'; - -/// Class to wrap the command line calls to git. -class GitCli { - Logger logger = Logger('RepositoryManager'); - - static const String git = 'git'; - - static const String repositoryHttpPrefix = 'https://github.com/'; - static const String repositorySshPrefix = 'git@github.com:'; - - late String repositoryPrefix; - - late CliCommand _cliCommand; - - GitCli(GitAccessMethod gitCloneMethod, CliCommand cliCommand) { - switch (gitCloneMethod) { - case GitAccessMethod.SSH: - repositoryPrefix = repositorySshPrefix; - break; - case GitAccessMethod.HTTP: - repositoryPrefix = repositoryHttpPrefix; - break; - } - _cliCommand = cliCommand; - } - - /// Check to see if the current directory is a git repository. - Future isGitRepository(String directory) async { - final ProcessResult processResult = await _cliCommand.runCliCommand( - executable: git, - arguments: [ - 'rev-parse', - ], - throwOnError: false, - workingDirectory: directory, - ); - - return processResult.exitCode == 0; - } - - /// Checkout repository if it does not currently exist on disk. - /// We will need to protect against multiple checkouts just in case multiple - /// calls occur at the same time. - Future cloneRepository({ - required RepositorySlug slug, - required String workingDirectory, - required String targetDirectory, - List? options, - bool throwOnError = true, - }) async { - final List clone = [ - 'clone', - '$repositoryPrefix${slug.fullName}', - targetDirectory, - ]; - if (options != null) { - clone.addAll(options); - } - final ProcessResult processResult = await _cliCommand.runCliCommand( - executable: git, - arguments: clone, - workingDirectory: workingDirectory, - throwOnError: throwOnError, - ); - - return processResult; - } - - Future setupUserConfig({ - required RepositorySlug slug, - required String workingDirectory, - bool throwOnError = true, - }) async { - return _cliCommand.runCliCommand( - executable: git, - arguments: [ - 'config', - '--global', - 'user.name', - '"auto-submit[bot]"', - ], - workingDirectory: workingDirectory, - throwOnError: throwOnError, - ); - } - - Future setupUserEmailConfig({ - required RepositorySlug slug, - required String workingDirectory, - bool throwOnError = true, - }) async { - return _cliCommand.runCliCommand( - executable: git, - arguments: [ - 'config', - '--global', - 'user.email', - // TODO not sure if the bot will end up needing a valid email. - // This might also be flutter-auto-submit-bot@google.com - '"flutter-engprod-team@google.com"', - ], - workingDirectory: workingDirectory, - throwOnError: throwOnError, - ); - } - - /// This is necessary with forked repos but may not be necessary with the bot - /// as the bot has direct access to the repository. - Future setUpstream({ - required RepositorySlug slug, - required String workingDirectory, - required String branchName, - required String token, - bool throwOnError = true, - }) async { - return _cliCommand.runCliCommand( - executable: git, - arguments: [ - 'remote', - 'set-url', - 'origin', - 'https://autosubmit[bot]:$token@github.com/${slug.fullName}.git', - ], - workingDirectory: workingDirectory, - throwOnError: throwOnError, - ); - } - - /// Fetch all new refs for the repository. - Future fetchAll({ - required String workingDirectory, - bool throwOnError = true, - }) async { - return _cliCommand.runCliCommand( - executable: git, - arguments: [ - 'fetch', - '--all', - ], - throwOnError: throwOnError, - ); - } - - Future pullRebase({ - required String? workingDirectory, - bool throwOnError = true, - }) async { - return _updateRepository( - workingDirectory: workingDirectory, - pullMethod: '--rebase', - throwOnError: throwOnError, - ); - } - - Future pullMerge({ - required String? workingDirectory, - bool throwOnError = true, - }) async { - return _updateRepository( - workingDirectory: workingDirectory, - pullMethod: '--merge', - throwOnError: throwOnError, - ); - } - - /// Run the git pull rebase command to keep the repository up to date. - Future _updateRepository({ - required String? workingDirectory, - required String pullMethod, - bool throwOnError = true, - }) async { - final ProcessResult processResult = await _cliCommand.runCliCommand( - executable: git, - arguments: [ - 'pull', - pullMethod, - ], - workingDirectory: workingDirectory, - ); - return processResult; - } - - /// Checkout and create a branch for the current edit. - /// - /// TODO The strategy may be unneccessary here as the bot will not have to - /// create its own fork of the repo. - Future createBranch({ - required String newBranchName, - required String workingDirectory, - bool useCheckout = false, - bool throwOnError = true, - }) async { - // Then create the new branch. - List args; - if (useCheckout) { - args = [ - 'checkout', - '-b', - newBranchName, - ]; - } else { - args = [ - 'branch', - newBranchName, - ]; - } - - return _cliCommand.runCliCommand( - executable: git, - arguments: args, - workingDirectory: workingDirectory, - throwOnError: throwOnError, - ); - } - - /// Revert a pull request commit. - Future revertChange({ - required String commitSha, - required String workingDirectory, - bool throwOnError = true, - }) async { - // Issue a revert of the pull request. - return _cliCommand.runCliCommand( - executable: git, - arguments: [ - 'revert', - '--no-edit', - '-m', - '1', - commitSha, - ], - workingDirectory: workingDirectory, - throwOnError: throwOnError, - ); - } - - /// Push changes made to the local branch to github. - Future pushBranch({ - required String branchName, - required String workingDirectory, - bool throwOnError = true, - }) async { - return _cliCommand.runCliCommand( - executable: git, - arguments: [ - 'push', - '--verbose', - 'origin', - branchName, - ], - workingDirectory: workingDirectory, - throwOnError: throwOnError, - ); - } - - /// Delete a local branch from the repo. - Future deleteLocalBranch({ - required String branchName, - required String workingDirectory, - bool throwOnError = true, - }) async { - return _cliCommand.runCliCommand( - executable: git, - arguments: [ - 'branch', - '-D', - branchName, - ], - workingDirectory: workingDirectory, - throwOnError: throwOnError, - ); - } - - /// Delete a remote branch from the repo. - /// - /// When merging a pull request the pr branch is not automatically deleted. - Future deleteRemoteBranch({ - required String branchName, - required String workingDirectory, - bool throwOnError = true, - }) async { - return _cliCommand.runCliCommand( - executable: git, - arguments: [ - 'push', - 'origin', - '--delete', - branchName, - ], - throwOnError: throwOnError, - ); - } - - /// Get the remote origin of the current repository. - Future showOriginUrl({ - required String workingDirectory, - bool throwOnError = true, - }) async { - return _cliCommand.runCliCommand( - executable: git, - arguments: [ - 'config', - '--get', - 'remote.origin.url', - ], - workingDirectory: workingDirectory, - throwOnError: throwOnError, - ); - } - - Future switchBranch({ - required String workingDirectory, - required String branchName, - bool throwOnError = true, - }) async { - return _cliCommand.runCliCommand( - executable: git, - arguments: [ - 'switch', - branchName, - ], - workingDirectory: workingDirectory, - throwOnError: throwOnError, - ); - } -} diff --git a/auto_submit/lib/git/git_repository_manager.dart b/auto_submit/lib/git/git_repository_manager.dart deleted file mode 100644 index b5402a70f..000000000 --- a/auto_submit/lib/git/git_repository_manager.dart +++ /dev/null @@ -1,101 +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:io'; -import 'package:auto_submit/service/log.dart'; -import 'package:github/github.dart'; -import 'git_cli.dart'; -import 'utilities.dart'; - -class GitRepositoryManager { - final RepositorySlug slug; - final String workingDirectory; - String? cloneToDirectory; - final GitCli gitCli; - - late String targetCloneDirectory; - - /// RepositoryManager will perform clone, revert and delete on the repository - /// in the working directory that is cloned to [cloneToDirectory]. - /// - /// If the clonedToDirectory is not provided then the name of the repository - /// will be used as the cloneToDirectory. - GitRepositoryManager({ - required this.slug, - //path/to/working/directory - required this.workingDirectory, - //reponame_commitSha - this.cloneToDirectory, - required this.gitCli, - }) { - cloneToDirectory ??= slug.name; - targetCloneDirectory = '$workingDirectory/$cloneToDirectory'; - } - - /// Clone the repository identified by the slug. - /// - /// Throw out rather than decomposing the return codes from the ProcessResult. - Future cloneRepository() async { - if (Directory(targetCloneDirectory).existsSync()) { - // Blow the directory away instead of trying to update it. - Directory(targetCloneDirectory).deleteSync(recursive: true); - } - - // Checking out a sparse copy will not checkout source files but will still - // allow a revert since we only care about the commitSha. - // Source: https://git-scm.com/docs/git-clone - await gitCli.cloneRepository( - slug: slug, - workingDirectory: workingDirectory, - targetDirectory: targetCloneDirectory, - options: ['--sparse'], - ); - } - - Future setupConfig() async { - await gitCli.setupUserConfig(slug: slug, workingDirectory: targetCloneDirectory); - await gitCli.setupUserEmailConfig(slug: slug, workingDirectory: targetCloneDirectory); - } - - /// Revert a commit in the current repository. - /// - /// The [baseBranchName] is the branch we want to branch from. In this case it - /// will almost always be the default branch name. The target branch is - /// preformatted with the commitSha. - Future revertCommit(String baseBranchName, String commitSha, RepositorySlug slug, String token) async { - final GitRevertBranchName revertBranchName = GitRevertBranchName(commitSha); - // Working directory for these must be repo checkout directory. - // Check out the baseBranchName before doing anything. - await gitCli.createBranch( - newBranchName: revertBranchName.branch, - workingDirectory: targetCloneDirectory, - useCheckout: true, - ); - - await gitCli.setUpstream( - slug: slug, - workingDirectory: targetCloneDirectory, - branchName: revertBranchName.branch, - token: token, - ); - - await gitCli.revertChange( - commitSha: commitSha, - workingDirectory: targetCloneDirectory, - ); - - await gitCli.pushBranch( - branchName: revertBranchName.branch, - workingDirectory: targetCloneDirectory, - ); - } - - /// Delete the repository managed by this instance. - Future deleteRepository() async { - log.info('Deleting clone directory $targetCloneDirectory'); - if (Directory(targetCloneDirectory).existsSync()) { - Directory(targetCloneDirectory).deleteSync(recursive: true); - } - } -} diff --git a/auto_submit/lib/git/utilities.dart b/auto_submit/lib/git/utilities.dart deleted file mode 100644 index 3bb873422..000000000 --- a/auto_submit/lib/git/utilities.dart +++ /dev/null @@ -1,22 +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. - -/// There are two ways to clone the repository which will be configurable in the -/// repository configuration in .github. -enum GitAccessMethod { - SSH, - HTTP, -} - -/// Wrapper class to create a revert branch that is comprised of the prefix -/// revert_ and the commit sha so the branch is easily identifiable. -class GitRevertBranchName { - final String _commitSha; - - const GitRevertBranchName(this._commitSha); - - static const String _branchPrefix = 'revert'; - - String get branch => '${_branchPrefix}_$_commitSha'; -} diff --git a/auto_submit/lib/helpers.dart b/auto_submit/lib/helpers.dart deleted file mode 100644 index 13240ec3f..000000000 --- a/auto_submit/lib/helpers.dart +++ /dev/null @@ -1,77 +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. - -import 'dart:async'; -import 'dart:io'; - -import 'package:shelf/shelf.dart'; -import 'package:shelf/shelf_io.dart'; - -import '../service/log.dart'; - -/// Serves [handler] on [InternetAddress.anyIPv4] using the port returned by -/// [listenPort]. -/// -/// The returned [Future] will complete using [terminateRequestFuture] after -/// closing the server. -Future serveHandler(Handler handler) async { - final int port = listenPort(); - - final HttpServer server = await serve( - handler, - InternetAddress.anyIPv4, // Allows external connections - port, - ); - log.info('Serving at http://${server.address.host}:${server.port}'); - - await terminateRequestFuture(); - - await server.close(); -} - -/// Returns the port to listen on from environment variable or uses the default -/// `8080`. -/// -/// See https://cloud.google.com/run/docs/reference/container-contract#port -int listenPort() => int.parse(Platform.environment['PORT'] ?? '8080'); - -/// Returns a [Future] that completes when the process receives a -/// [ProcessSignal] requesting a shutdown. -/// -/// [ProcessSignal.sigint] is listened to on all platforms. -/// -/// [ProcessSignal.sigterm] is listened to on all platforms except Windows. -Future terminateRequestFuture() { - final Completer completer = Completer.sync(); - - // sigIntSub is copied below to avoid a race condition - ignoring this lint - // ignore: cancel_subscriptions - StreamSubscription? sigIntSub, sigTermSub; - - Future signalHandler(ProcessSignal signal) async { - log.info('Received signal $signal - closing'); - - final subCopy = sigIntSub; - if (subCopy != null) { - sigIntSub = null; - await subCopy.cancel(); - sigIntSub = null; - if (sigTermSub != null) { - await sigTermSub!.cancel(); - sigTermSub = null; - } - completer.complete(true); - } - } - - sigIntSub = ProcessSignal.sigint.watch().listen(signalHandler); - - // SIGTERM is not supported on Windows. Attempting to register a SIGTERM - // handler raises an exception. - if (!Platform.isWindows) { - sigTermSub = ProcessSignal.sigterm.watch().listen(signalHandler); - } - - return completer.future; -} diff --git a/auto_submit/lib/model/auto_submit_query_result.dart b/auto_submit/lib/model/auto_submit_query_result.dart deleted file mode 100644 index f83d59e95..000000000 --- a/auto_submit/lib/model/auto_submit_query_result.dart +++ /dev/null @@ -1,241 +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. - -import 'dart:convert'; - -import 'package:json_annotation/json_annotation.dart'; - -part 'auto_submit_query_result.g.dart'; - -/// The classes in this file are used to serialize/deserialize graphql results. -/// Using classes rather than complex maps improves the readability of the code -/// and makes possible to define an extensible interface for the validations. - -@JsonSerializable() -class Author { - Author({ - this.login, - }); - final String? login; - - factory Author.fromJson(Map json) => _$AuthorFromJson(json); - - Map toJson() => _$AuthorToJson(this); -} - -@JsonSerializable() -class ReviewNode { - ReviewNode({ - this.author, - this.authorAssociation, - this.state, - }); - final Author? author; - @JsonKey(name: 'authorAssociation') - final String? authorAssociation; - final String? state; - - factory ReviewNode.fromJson(Map json) => _$ReviewNodeFromJson(json); - - Map toJson() => _$ReviewNodeToJson(this); -} - -@JsonSerializable() -class Reviews { - Reviews({this.nodes}); - - List? nodes; - - factory Reviews.fromJson(Map json) => _$ReviewsFromJson(json); - - Map toJson() => _$ReviewsToJson(this); -} - -@JsonSerializable() -class CommitNode { - CommitNode({this.commit}); - - Commit? commit; - - factory CommitNode.fromJson(Map json) => _$CommitNodeFromJson(json); - - Map toJson() => _$CommitNodeToJson(this); -} - -@JsonSerializable() -class Commits { - Commits({this.nodes}); - - List? nodes; - - factory Commits.fromJson(Map json) => _$CommitsFromJson(json); - - Map toJson() => _$CommitsToJson(this); -} - -enum MergeableState { - CONFLICTING, - MERGEABLE, - UNKNOWN, -} - -@JsonSerializable() -class ContextNode { - ContextNode({ - this.context, - this.state, - this.targetUrl, - }); - - String? context; - String? state; - @JsonKey(name: 'targetUrl') - String? targetUrl; - - factory ContextNode.fromJson(Map json) => _$ContextNodeFromJson(json); - - Map toJson() => _$ContextNodeToJson(this); - - @override - String toString() => jsonEncode(_$ContextNodeToJson(this)); -} - -@JsonSerializable() -class Status { - Status({this.contexts}); - - List? contexts; - - factory Status.fromJson(Map json) => _$StatusFromJson(json); - - Map toJson() => _$StatusToJson(this); -} - -@JsonSerializable() -class Commit { - Commit({ - this.abbreviatedOid, - this.oid, - this.committedDate, - this.pushedDate, - this.status, - }); - @JsonKey(name: 'abbreviatedOid') - final String? abbreviatedOid; - final String? oid; - @JsonKey(name: 'committedDate') - final DateTime? committedDate; - @JsonKey(name: 'pushedDate') - final DateTime? pushedDate; - final Status? status; - - factory Commit.fromJson(Map json) => _$CommitFromJson(json); - - Map toJson() => _$CommitToJson(this); -} - -@JsonSerializable() -class PullRequest { - PullRequest({ - this.author, - this.authorAssociation, - this.id, - this.title, - this.body, - this.reviews, - this.commits, - this.mergeable, - this.number, - }); - final Author? author; - @JsonKey(name: 'authorAssociation') - final String? authorAssociation; - final String? id; - final String? title; - final String? body; - final Reviews? reviews; - final Commits? commits; - final int? number; - // https://docs.github.com/en/graphql/reference/enums#mergeablestate - final MergeableState? mergeable; - - factory PullRequest.fromJson(Map json) => _$PullRequestFromJson(json); - - Map toJson() => _$PullRequestToJson(this); -} - -@JsonSerializable() -class Repository { - Repository({ - this.pullRequest, - }); - - @JsonKey(name: 'pullRequest') - PullRequest? pullRequest; - - factory Repository.fromJson(Map json) => _$RepositoryFromJson(json); - - Map toJson() => _$RepositoryToJson(this); -} - -@JsonSerializable() -class QueryResult { - QueryResult({ - this.repository, - }); - - Repository? repository; - - factory QueryResult.fromJson(Map json) => _$QueryResultFromJson(json); - - Map toJson() => _$QueryResultToJson(this); -} - -/// The reason for this funky naming scheme can be blamed on GitHub. -/// -/// See: https://docs.github.com/en/graphql/reference/mutations#revertpullrequest -/// The enclosing object is called RevertPullRequest and has a nested field also -/// called RevertPullRequest. -@JsonSerializable() -class RevertPullRequest { - RevertPullRequest({ - this.clientMutationId, - this.pullRequest, - this.revertPullRequest, - }); - - @JsonKey(name: 'clientMutationId') - String? clientMutationId; - @JsonKey(name: 'pullRequest') - PullRequest? pullRequest; - @JsonKey(name: 'revertPullRequest') - PullRequest? revertPullRequest; - - factory RevertPullRequest.fromJson(Map json) => _$RevertPullRequestFromJson(json); - - Map toJson() => _$RevertPullRequestToJson(this); -} - -/// This is needed since the data we get is buried within this outer object and -/// to simplify the deserialization need this wrapper. -/// -/// The return data is nested as such: -/// "data": { -/// "revertPullRequest": { -/// "clientMutationId": xxx, -/// "pullRequest": { ... }, -/// "revertPullRequest": { ... } -/// } -/// } -@JsonSerializable() -class RevertPullRequestData { - RevertPullRequestData({this.revertPullRequest}); - - @JsonKey(name: 'revertPullRequest') - RevertPullRequest? revertPullRequest; - - factory RevertPullRequestData.fromJson(Map json) => _$RevertPullRequestDataFromJson(json); - - Map toJson() => _$RevertPullRequestDataToJson(this); -} diff --git a/auto_submit/lib/model/auto_submit_query_result.g.dart b/auto_submit/lib/model/auto_submit_query_result.g.dart deleted file mode 100644 index d2d19b220..000000000 --- a/auto_submit/lib/model/auto_submit_query_result.g.dart +++ /dev/null @@ -1,160 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'auto_submit_query_result.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -Author _$AuthorFromJson(Map json) => Author( - login: json['login'] as String?, - ); - -Map _$AuthorToJson(Author instance) => { - 'login': instance.login, - }; - -ReviewNode _$ReviewNodeFromJson(Map json) => ReviewNode( - author: json['author'] == null ? null : Author.fromJson(json['author'] as Map), - authorAssociation: json['authorAssociation'] as String?, - state: json['state'] as String?, - ); - -Map _$ReviewNodeToJson(ReviewNode instance) => { - 'author': instance.author, - 'authorAssociation': instance.authorAssociation, - 'state': instance.state, - }; - -Reviews _$ReviewsFromJson(Map json) => Reviews( - nodes: (json['nodes'] as List?)?.map((e) => ReviewNode.fromJson(e as Map)).toList(), - ); - -Map _$ReviewsToJson(Reviews instance) => { - 'nodes': instance.nodes, - }; - -CommitNode _$CommitNodeFromJson(Map json) => CommitNode( - commit: json['commit'] == null ? null : Commit.fromJson(json['commit'] as Map), - ); - -Map _$CommitNodeToJson(CommitNode instance) => { - 'commit': instance.commit, - }; - -Commits _$CommitsFromJson(Map json) => Commits( - nodes: (json['nodes'] as List?)?.map((e) => CommitNode.fromJson(e as Map)).toList(), - ); - -Map _$CommitsToJson(Commits instance) => { - 'nodes': instance.nodes, - }; - -ContextNode _$ContextNodeFromJson(Map json) => ContextNode( - context: json['context'] as String?, - state: json['state'] as String?, - targetUrl: json['targetUrl'] as String?, - ); - -Map _$ContextNodeToJson(ContextNode instance) => { - 'context': instance.context, - 'state': instance.state, - 'targetUrl': instance.targetUrl, - }; - -Status _$StatusFromJson(Map json) => Status( - contexts: - (json['contexts'] as List?)?.map((e) => ContextNode.fromJson(e as Map)).toList(), - ); - -Map _$StatusToJson(Status instance) => { - 'contexts': instance.contexts, - }; - -Commit _$CommitFromJson(Map json) => Commit( - abbreviatedOid: json['abbreviatedOid'] as String?, - oid: json['oid'] as String?, - committedDate: json['committedDate'] == null ? null : DateTime.parse(json['committedDate'] as String), - pushedDate: json['pushedDate'] == null ? null : DateTime.parse(json['pushedDate'] as String), - status: json['status'] == null ? null : Status.fromJson(json['status'] as Map), - ); - -Map _$CommitToJson(Commit instance) => { - 'abbreviatedOid': instance.abbreviatedOid, - 'oid': instance.oid, - 'committedDate': instance.committedDate?.toIso8601String(), - 'pushedDate': instance.pushedDate?.toIso8601String(), - 'status': instance.status, - }; - -PullRequest _$PullRequestFromJson(Map json) => PullRequest( - author: json['author'] == null ? null : Author.fromJson(json['author'] as Map), - authorAssociation: json['authorAssociation'] as String?, - id: json['id'] as String?, - title: json['title'] as String?, - body: json['body'] as String?, - reviews: json['reviews'] == null ? null : Reviews.fromJson(json['reviews'] as Map), - commits: json['commits'] == null ? null : Commits.fromJson(json['commits'] as Map), - mergeable: $enumDecodeNullable(_$MergeableStateEnumMap, json['mergeable']), - number: json['number'] as int?, - ); - -Map _$PullRequestToJson(PullRequest instance) => { - 'author': instance.author, - 'authorAssociation': instance.authorAssociation, - 'id': instance.id, - 'title': instance.title, - 'body': instance.body, - 'reviews': instance.reviews, - 'commits': instance.commits, - 'number': instance.number, - 'mergeable': _$MergeableStateEnumMap[instance.mergeable], - }; - -const _$MergeableStateEnumMap = { - MergeableState.CONFLICTING: 'CONFLICTING', - MergeableState.MERGEABLE: 'MERGEABLE', - MergeableState.UNKNOWN: 'UNKNOWN', -}; - -Repository _$RepositoryFromJson(Map json) => Repository( - pullRequest: - json['pullRequest'] == null ? null : PullRequest.fromJson(json['pullRequest'] as Map), - ); - -Map _$RepositoryToJson(Repository instance) => { - 'pullRequest': instance.pullRequest, - }; - -QueryResult _$QueryResultFromJson(Map json) => QueryResult( - repository: json['repository'] == null ? null : Repository.fromJson(json['repository'] as Map), - ); - -Map _$QueryResultToJson(QueryResult instance) => { - 'repository': instance.repository, - }; - -RevertPullRequest _$RevertPullRequestFromJson(Map json) => RevertPullRequest( - clientMutationId: json['clientMutationId'] as String?, - pullRequest: - json['pullRequest'] == null ? null : PullRequest.fromJson(json['pullRequest'] as Map), - revertPullRequest: json['revertPullRequest'] == null - ? null - : PullRequest.fromJson(json['revertPullRequest'] as Map), - ); - -Map _$RevertPullRequestToJson(RevertPullRequest instance) => { - 'clientMutationId': instance.clientMutationId, - 'pullRequest': instance.pullRequest, - 'revertPullRequest': instance.revertPullRequest, - }; - -RevertPullRequestData _$RevertPullRequestDataFromJson(Map json) => RevertPullRequestData( - revertPullRequest: json['revertPullRequest'] == null - ? null - : RevertPullRequest.fromJson(json['revertPullRequest'] as Map), - ); - -Map _$RevertPullRequestDataToJson(RevertPullRequestData instance) => { - 'revertPullRequest': instance.revertPullRequest, - }; diff --git a/auto_submit/lib/model/big_query_pull_request_record.dart b/auto_submit/lib/model/big_query_pull_request_record.dart deleted file mode 100644 index b435f3403..000000000 --- a/auto_submit/lib/model/big_query_pull_request_record.dart +++ /dev/null @@ -1,41 +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. - -import 'dart:convert'; - -import 'package:json_annotation/json_annotation.dart'; -import 'package:meta/meta.dart'; - -part 'big_query_pull_request_record.g.dart'; - -@immutable -@JsonSerializable() -class PullRequestRecord { - const PullRequestRecord({ - this.prCreatedTimestamp, - this.prLandedTimestamp, - this.organization, - this.repository, - this.author, - this.prNumber, - this.prCommit, - this.prRequestType, - }); - - final DateTime? prCreatedTimestamp; - final DateTime? prLandedTimestamp; - final String? organization; - final String? repository; - final String? author; - final int? prNumber; - final String? prCommit; - final String? prRequestType; - - @override - String toString() => jsonEncode(toJson()); - - factory PullRequestRecord.fromJson(Map json) => _$PullRequestRecordFromJson(json); - - Map toJson() => _$PullRequestRecordToJson(this); -} diff --git a/auto_submit/lib/model/big_query_pull_request_record.g.dart b/auto_submit/lib/model/big_query_pull_request_record.g.dart deleted file mode 100644 index a898c801e..000000000 --- a/auto_submit/lib/model/big_query_pull_request_record.g.dart +++ /dev/null @@ -1,31 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'big_query_pull_request_record.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -PullRequestRecord _$PullRequestRecordFromJson(Map json) => PullRequestRecord( - prCreatedTimestamp: - json['pr_created_timestamp'] == null ? null : DateTime.parse(json['pr_created_timestamp'] as String), - prLandedTimestamp: - json['pr_landed_timestamp'] == null ? null : DateTime.parse(json['pr_landed_timestamp'] as String), - organization: json['organization'] as String?, - repository: json['repository'] as String?, - author: json['author'] as String?, - prNumber: json['pr_number'] as int?, - prCommit: json['pr_commit'] as String?, - prRequestType: json['pr_request_type'] as String?, - ); - -Map _$PullRequestRecordToJson(PullRequestRecord instance) => { - 'pr_created_timestamp': instance.prCreatedTimestamp?.toIso8601String(), - 'pr_landed_timestamp': instance.prLandedTimestamp?.toIso8601String(), - 'organization': instance.organization, - 'repository': instance.repository, - 'author': instance.author, - 'pr_number': instance.prNumber, - 'pr_commit': instance.prCommit, - 'pr_request_type': instance.prRequestType, - }; diff --git a/auto_submit/lib/model/pull_request_data_types.dart b/auto_submit/lib/model/pull_request_data_types.dart deleted file mode 100644 index e85a5bfc1..000000000 --- a/auto_submit/lib/model/pull_request_data_types.dart +++ /dev/null @@ -1,19 +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. - -/// The type of the change in the pull request we have processed. -enum PullRequestChangeType { - /// Merge is any submitted pull request change that does not undo previous changes. - change, - - /// Revert is specifically for undoing changes. - revert, -} - -/// Values representing the current states of a pull requests we process with -/// the autosubmit service. -enum PullRequestState { - open, - closed, -} diff --git a/auto_submit/lib/request_handling/authentication.dart b/auto_submit/lib/request_handling/authentication.dart deleted file mode 100644 index d17832a04..000000000 --- a/auto_submit/lib/request_handling/authentication.dart +++ /dev/null @@ -1,44 +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:meta/meta.dart'; -import 'package:shelf/shelf.dart'; - -import '../requests/exceptions.dart'; - -/// Class capable of authenticating [HttpRequest]s. -/// -/// If the request has the `'X-Appengine-Cron'` HTTP header set to "true", -/// then the request will be authenticated as an App Engine cron job. -/// -/// The `'X-Appengine-Cron'` HTTP header is set automatically by App Engine -/// and will be automatically stripped from the request by the App Engine -/// runtime if the request originated from anything other than a cron job. -/// Thus, the header is safe to trust as an authentication indicator. -/// -/// See also: -/// -/// * -@immutable -// TODO(Kristin): Generalize this to implement from a AuthProvider. https://github.com/flutter/flutter/issues/101614 -class CronAuthProvider { - const CronAuthProvider(); - - /// Authenticates the specified [request]. - /// - /// This will throw an [Unauthenticated] exception if the request is - /// unauthenticated. - Future authenticate(Request request) async { - final Map reqHeader = request.headers; - final bool isCron = reqHeader['X-Appengine-Cron'] == 'true'; - if (isCron) { - // Authenticate cron requests - return true; - } - throw const Unauthenticated('User is not signed in'); - } -} diff --git a/auto_submit/lib/request_handling/pubsub.dart b/auto_submit/lib/request_handling/pubsub.dart deleted file mode 100644 index 6b3c1a551..000000000 --- a/auto_submit/lib/request_handling/pubsub.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:convert'; - -import 'package:auto_submit/service/config.dart'; -import 'package:googleapis/pubsub/v1.dart' as pubsub; -import 'package:googleapis_auth/auth_io.dart'; -import 'package:http/http.dart'; - -import '../foundation/providers.dart'; -import '../foundation/typedefs.dart'; -import '../service/log.dart'; - -/// Service class for interacting with PubSub. -class PubSub { - const PubSub({ - this.httpClientProvider = Providers.freshHttpClient, - }); - - final HttpClientProvider httpClientProvider; - - /// Adds one message to the topic. - Future publish(String topic, dynamic json) async { - final Client httpClient = await clientViaApplicationDefaultCredentials( - scopes: [ - pubsub.PubsubApi.pubsubScope, - ], - ); - final pubsub.PubsubApi pubsubApi = pubsub.PubsubApi(httpClient); - final String messageData = jsonEncode(json); - final List messageBytes = utf8.encode(messageData); - final String messageBase64 = base64Encode(messageBytes); - final pubsub.PublishRequest request = pubsub.PublishRequest( - messages: [ - pubsub.PubsubMessage(data: messageBase64), - ], - ); - final String fullTopicName = '${Config.pubsubTopicsPrefix}/$topic'; - final pubsub.PublishResponse response = await pubsubApi.projects.topics.publish(request, fullTopicName); - log.info('pubsub response messageId=${response.messageIds}'); - } - - /// Pulls messages from the server. - Future pull(String subscription, int maxMessages) async { - final Client httpClient = await clientViaApplicationDefaultCredentials( - scopes: [ - pubsub.PubsubApi.pubsubScope, - ], - ); - final pubsub.PubsubApi pubsubApi = pubsub.PubsubApi(httpClient); - final pubsub.PullRequest pullRequest = pubsub.PullRequest(maxMessages: maxMessages); - final pubsub.PullResponse pullResponse = - await pubsubApi.projects.subscriptions.pull(pullRequest, '${Config.pubsubSubscriptionsPrefix}/$subscription'); - return pullResponse; - } - - /// Acknowledges the messages associated with the `ack_ids` in the `AcknowledgeRequest`. - /// - /// The PubSub system can remove the relevant messages from the subscription. - Future acknowledge(String subscription, String ackId) async { - final Client httpClient = await clientViaApplicationDefaultCredentials( - scopes: [ - pubsub.PubsubApi.pubsubScope, - ], - ); - final pubsub.PubsubApi pubsubApi = pubsub.PubsubApi(httpClient); - final List ackIds = [ackId]; - final pubsub.AcknowledgeRequest acknowledgeRequest = pubsub.AcknowledgeRequest(ackIds: ackIds); - await pubsubApi.projects.subscriptions - .acknowledge(acknowledgeRequest, '${Config.pubsubSubscriptionsPrefix}/$subscription'); - } -} diff --git a/auto_submit/lib/requests/check_pull_request.dart b/auto_submit/lib/requests/check_pull_request.dart deleted file mode 100644 index ba46c42f7..000000000 --- a/auto_submit/lib/requests/check_pull_request.dart +++ /dev/null @@ -1,101 +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. - -import 'dart:async'; -import 'dart:convert'; - -import 'package:auto_submit/requests/check_request.dart'; -import 'package:auto_submit/service/approver_service.dart'; -import 'package:auto_submit/service/log.dart'; -import 'package:auto_submit/service/pull_request_validation_service.dart'; -import 'package:github/github.dart'; -import 'package:googleapis/pubsub/v1.dart' as pub; -import 'package:shelf/shelf.dart'; - -import '../request_handling/pubsub.dart'; - -/// Handler for processing pull requests with 'autosubmit' label. -/// -/// For pull requests where an 'autosubmit' label was added in pubsub, -/// check if the pull request is mergable. -class CheckPullRequest extends CheckRequest { - const CheckPullRequest({ - required super.config, - required super.cronAuthProvider, - super.approverProvider = ApproverService.defaultProvider, - super.pubsub = const PubSub(), - }); - - @override - Future get() async { - return process( - config.pubsubPullRequestSubscription, - config.kPubsubPullNumber, - config.kPullMesssageBatchSize, - ); - } - - ///TODO refactor this method out into the base class. - /// Process pull request messages from Pubsub. - Future process( - String pubSubSubscription, - int pubSubPulls, - int pubSubBatchSize, - ) async { - final Set processingLog = {}; - final List messageList = await pullMessages( - pubSubSubscription, - pubSubPulls, - pubSubBatchSize, - ); - if (messageList.isEmpty) { - log.info('No messages are pulled.'); - return Response.ok('No messages are pulled.'); - } - - log.info('Processing ${messageList.length} messages'); - - final PullRequestValidationService validationService = PullRequestValidationService(config); - - final List> futures = >[]; - - for (pub.ReceivedMessage message in messageList) { - log.info(message.toJson()); - assert(message.message != null); - assert(message.message!.data != null); - final String messageData = message.message!.data!; - - final Map rawBody = - json.decode(String.fromCharCodes(base64.decode(messageData))) as Map; - log.info('request raw body = $rawBody'); - - final PullRequest pullRequest = PullRequest.fromJson(rawBody); - - log.info('Processing message ackId: ${message.ackId}'); - log.info('Processing mesageId: ${message.message!.messageId}'); - log.info('Processing PR: $rawBody'); - if (processingLog.contains(pullRequest.number)) { - // Ack duplicate. - log.info('Ack the duplicated message : ${message.ackId!}.'); - await pubsub.acknowledge( - pubSubSubscription, - message.ackId!, - ); - - continue; - } else { - final ApproverService approver = approverProvider(config); - log.info('Checking auto approval of pull request: $rawBody'); - await approver.autoApproval(pullRequest); - processingLog.add(pullRequest.number!); - } - - futures.add( - validationService.processMessage(pullRequest, message.ackId!, pubsub), - ); - } - await Future.wait(futures); - return Response.ok('Finished processing changes'); - } -} diff --git a/auto_submit/lib/requests/check_request.dart b/auto_submit/lib/requests/check_request.dart deleted file mode 100644 index 8a8f04ba7..000000000 --- a/auto_submit/lib/requests/check_request.dart +++ /dev/null @@ -1,52 +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:auto_submit/server/authenticated_request_handler.dart'; -import 'package:auto_submit/service/approver_service.dart'; -import 'package:googleapis/pubsub/v1.dart' as pub; -import 'package:shelf/shelf.dart'; - -import '../request_handling/pubsub.dart'; - -abstract class CheckRequest extends AuthenticatedRequestHandler { - const CheckRequest({ - required super.config, - required super.cronAuthProvider, - this.approverProvider = ApproverService.defaultProvider, - this.pubsub = const PubSub(), - }); - - final PubSub pubsub; - final ApproverServiceProvider approverProvider; - - @override - Future get(); - - /// Pulls queued Pub/Sub messages. - /// - /// Pub/Sub pull request API doesn't guarantee returning all messages each time. This - /// loops to pull `kPubsubPullNumber` times to try covering all queued messages. - Future> pullMessages( - String subscription, - int pulls, - int batchSize, - ) async { - final Map messageMap = {}; - for (int i = 0; i < pulls; i++) { - final pub.PullResponse pullResponse = await pubsub.pull( - subscription, - batchSize, - ); - final List? receivedMessages = pullResponse.receivedMessages; - if (receivedMessages == null) { - continue; - } - for (pub.ReceivedMessage message in receivedMessages) { - final String messageId = message.message!.messageId!; - messageMap[messageId] = message; - } - } - return messageMap.values.toList(); - } -} diff --git a/auto_submit/lib/requests/check_revert_request.dart b/auto_submit/lib/requests/check_revert_request.dart deleted file mode 100644 index 1578a689b..000000000 --- a/auto_submit/lib/requests/check_revert_request.dart +++ /dev/null @@ -1,105 +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:auto_submit/request_handling/pubsub.dart'; -import 'package:auto_submit/requests/check_request.dart'; -import 'package:auto_submit/requests/github_pull_request_event.dart'; -import 'package:auto_submit/service/approver_service.dart'; -import 'package:auto_submit/service/log.dart'; -import 'package:auto_submit/service/revert_request_validation_service.dart'; -import 'package:github/github.dart'; -import 'package:googleapis/pubsub/v1.dart' as pub; -import 'package:shelf/shelf.dart'; - -/// Handler for processing pull requests with 'revert' label. -/// -/// For pull requests where an 'revert' label was added in pubsub, -/// check if the revert request is mergable. -class CheckRevertRequest extends CheckRequest { - const CheckRevertRequest({ - required super.config, - required super.cronAuthProvider, - super.approverProvider = ApproverService.defaultProvider, - super.pubsub = const PubSub(), - }); - - @override - Future get() async { - /// Currently this is unused and cannot be called. - return process( - config.pubsubRevertRequestSubscription, - config.kPubsubPullNumber, - config.kPullMesssageBatchSize, - ); - } - - /// Process pull request messages from Pubsub. - Future process( - String pubSubSubscription, - int pubSubPulls, - int pubSubBatchSize, - ) async { - final Set processingLog = {}; - final List messageList = await pullMessages( - pubSubSubscription, - pubSubPulls, - pubSubBatchSize, - ); - if (messageList.isEmpty) { - log.info('No messages are pulled.'); - return Response.ok('No messages are pulled.'); - } - - log.info('Processing ${messageList.length} messages'); - - final RevertRequestValidationService validationService = RevertRequestValidationService(config); - - final List> futures = >[]; - - for (pub.ReceivedMessage message in messageList) { - log.info(message.toJson()); - assert(message.message != null); - assert(message.message!.data != null); - final String messageData = message.message!.data!; - - final Map rawBody = - json.decode(String.fromCharCodes(base64.decode(messageData))) as Map; - - final GithubPullRequestEvent githubPullRequestEvent = GithubPullRequestEvent.fromJson(rawBody); - final PullRequest pullRequest = githubPullRequestEvent.pullRequest!; - - log.info('Processing message ackId: ${message.ackId}'); - log.info('Processing mesageId: ${message.message!.messageId}'); - log.info('Processing PR: $rawBody'); - if (processingLog.contains(pullRequest.number) || githubPullRequestEvent.action != 'labeled') { - // Ack duplicate. - log.info('Ack the duplicated message : ${message.ackId!}.'); - log.info('duplicate pull request #${pullRequest.number}'); - await pubsub.acknowledge(pubSubSubscription, message.ackId!); - continue; - } else { - // Use the auto approval as we do not want to allow non bot reverts to - // be processed throught the service. - log.info('new pull request #${pullRequest.number}'); - if (pullRequest.labels!.any((element) => element.name == 'revert of')) { - final ApproverService approver = approverProvider(config); - log.info('Checking auto approval of "revert of" pull request: $rawBody'); - await approver.autoApproval(pullRequest); - } else { - // These should be closed requests that do not need to be reviewed. - log.info('Processing "revert" request : ${pullRequest.number}.'); - } - processingLog.add(pullRequest.number!); - } - - futures.add( - validationService.processMessage(githubPullRequestEvent, message.ackId!, pubsub), - ); - } - await Future.wait(futures); - return Response.ok('Finished processing changes'); - } -} diff --git a/auto_submit/lib/requests/exceptions.dart b/auto_submit/lib/requests/exceptions.dart deleted file mode 100644 index e40c436cb..000000000 --- a/auto_submit/lib/requests/exceptions.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 'dart:io'; - -/// An exception that may be thrown by a [RequestHandler] to trigger an error -/// HTTP response. -class HttpStatusException implements Exception { - /// Creates a new [HttpStatusException]. - const HttpStatusException(this.statusCode, this.message); - - /// The HTTP status code to return to the issuer. - final int statusCode; - - /// The message to show to the issuer to explain the error. - final String message; - - @override - String toString() => 'HTTP $statusCode: $message'; -} - -/// Exception that will trigger an HTTP 400 bad request. -class BadRequestException extends HttpStatusException { - const BadRequestException([String message = 'Bad request']) : super(HttpStatus.badRequest, message); -} - -/// Exception that will trigger an HTTP 404 not found -class NotFoundException extends HttpStatusException { - const NotFoundException(String missing) : super(HttpStatus.notFound, 'Not found: $missing'); -} - -/// Exception that will trigger an HTTP 405 method not allowed. -class MethodNotAllowed extends HttpStatusException { - const MethodNotAllowed(String method) : super(HttpStatus.methodNotAllowed, 'Unsupported method: $method'); -} - -/// Exception that will trigger an HTTP 409 conflict. -class ConflictException extends HttpStatusException { - const ConflictException([String message = 'Request conflict with server state']) - : super(HttpStatus.conflict, message); -} - -/// Exception that will trigger an HTTP 500 internal server error. -class InternalServerError extends HttpStatusException { - const InternalServerError([String message = 'Internal server error']) - : super(HttpStatus.internalServerError, message); -} - -/// Exception that will trigger an HTTP 401 not authorized. -class Unauthorized extends HttpStatusException { - const Unauthorized([String message = 'Unauthorized']) : super(HttpStatus.unauthorized, message); -} - -/// Exception that will trigger an HTTP 403 forbidden. -class Forbidden extends HttpStatusException { - const Forbidden([String message = 'Forbidden']) : super(HttpStatus.forbidden, message); -} - -/// Exception thrown when attempting to authenticate a request that cannot be -/// authenticated. -class Unauthenticated implements Exception { - const Unauthenticated(this.message); - - final String message; - - @override - String toString() => 'Unauthenticated: $message'; -} diff --git a/auto_submit/lib/requests/github_pull_request_event.dart b/auto_submit/lib/requests/github_pull_request_event.dart deleted file mode 100644 index 00f977c4c..000000000 --- a/auto_submit/lib/requests/github_pull_request_event.dart +++ /dev/null @@ -1,34 +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:github/github.dart'; -import 'package:json_annotation/json_annotation.dart'; - -part 'github_pull_request_event.g.dart'; - -/// PullRequestMessage is a wrapper that keeps the sender and action of the event -/// sent to the webhook. -@JsonSerializable() -class GithubPullRequestEvent { - const GithubPullRequestEvent({ - this.pullRequest, - this.action, - this.sender, - }); - - /// The [PullRequest] object information. - @JsonKey(name: 'pull_request') - final PullRequest? pullRequest; - - /// The action as used by github for a [PullRequest] event. - final String? action; - - /// The author login of the person who initiated the event, currently only - /// useful when processing revert requests. - final User? sender; - - factory GithubPullRequestEvent.fromJson(Map json) => _$GithubPullRequestEventFromJson(json); - - Map toJson() => _$GithubPullRequestEventToJson(this); -} diff --git a/auto_submit/lib/requests/github_pull_request_event.g.dart b/auto_submit/lib/requests/github_pull_request_event.g.dart deleted file mode 100644 index 7309e50ea..000000000 --- a/auto_submit/lib/requests/github_pull_request_event.g.dart +++ /dev/null @@ -1,20 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'github_pull_request_event.dart'; - -// ************************************************************************** -// JsonSerializableGenerator -// ************************************************************************** - -GithubPullRequestEvent _$GithubPullRequestEventFromJson(Map json) => GithubPullRequestEvent( - pullRequest: - json['pull_request'] == null ? null : PullRequest.fromJson(json['pull_request'] as Map), - action: json['action'] as String?, - sender: json['sender'] == null ? null : User.fromJson(json['sender'] as Map), - ); - -Map _$GithubPullRequestEventToJson(GithubPullRequestEvent instance) => { - 'pull_request': instance.pullRequest, - 'action': instance.action, - 'sender': instance.sender, - }; diff --git a/auto_submit/lib/requests/github_webhook.dart b/auto_submit/lib/requests/github_webhook.dart deleted file mode 100644 index 90044cb60..000000000 --- a/auto_submit/lib/requests/github_webhook.dart +++ /dev/null @@ -1,104 +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. - -import 'dart:async'; -import 'dart:convert'; - -import 'package:auto_submit/requests/github_pull_request_event.dart'; -import 'package:github/github.dart'; -import 'package:shelf/shelf.dart'; -import 'package:crypto/crypto.dart'; - -import '../request_handling/pubsub.dart'; -import '../service/config.dart'; -import '../service/log.dart'; -import '../server/request_handler.dart'; -import '../requests/exceptions.dart'; - -/// Handler for processing GitHub webhooks. -/// -/// On events where an 'autosubmit' label was added to a pull request, -/// check if the pull request is mergable and publish to pubsub. -class GithubWebhook extends RequestHandler { - const GithubWebhook({ - required super.config, - this.pubsub = const PubSub(), - }); - - final PubSub pubsub; - - static const String pullRequest = 'pull_request'; - static const String labels = 'labels'; - static const String action = 'action'; - static const String sender = 'sender'; - - static const String eventTypeHeader = 'X-GitHub-Event'; - static const String signatureHeader = 'X-Hub-Signature'; - - @override - Future post(Request request) async { - final Map reqHeader = request.headers; - log.info('Header: $reqHeader'); - - final String? gitHubEvent = request.headers[GithubWebhook.eventTypeHeader]; - - if (gitHubEvent == null || request.headers[GithubWebhook.signatureHeader] == null) { - throw const BadRequestException('Missing required headers.'); - } - final List requestBytes = await request.read().expand((_) => _).toList(); - final String? hmacSignature = request.headers[GithubWebhook.signatureHeader]; - if (!await _validateRequest(hmacSignature, requestBytes)) { - log.info('User is forbidden'); - throw const Forbidden(); - } - - bool hasAutosubmit = false; - bool hasRevertLabel = false; - final String rawBody = utf8.decode(requestBytes); - final Map body = json.decode(rawBody) as Map; - - if (!body.containsKey(GithubWebhook.pullRequest) || - !((body[GithubWebhook.pullRequest] as Map).containsKey(GithubWebhook.labels))) { - return Response.ok(jsonEncode({})); - } - - final PullRequest pullRequest = PullRequest.fromJson(body[GithubWebhook.pullRequest] as Map); - final String action = body[GithubWebhook.action]; - final User sender = User.fromJson(body[GithubWebhook.sender] as Map); - - hasAutosubmit = pullRequest.labels!.any((label) => label.name == Config.kAutosubmitLabel); - hasRevertLabel = - pullRequest.labels!.any((label) => label.name == Config.kRevertLabel || label.name == Config.kRevertOfLabel); - - // Check for revert label first. - if (hasRevertLabel) { - log.info('Found pull request with the revert label.'); - await pubsub.publish( - config.pubsubRevertRequestTopic, - GithubPullRequestEvent( - pullRequest: pullRequest, - action: action, - sender: sender, - ), - ); - } else if (hasAutosubmit) { - log.info('Found pull request with autosubmit label.'); - await pubsub.publish(config.pubsubPullRequestTopic, pullRequest); - } - - return Response.ok(rawBody); - } - - Future _validateRequest( - String? signature, - List requestBody, - ) async { - final String rawKey = await config.getWebhookKey(); - final List key = utf8.encode(rawKey); - final Hmac hmac = Hmac(sha1, key); - final Digest digest = hmac.convert(requestBody); - final String bodySignature = 'sha1=$digest'; - return bodySignature == signature; - } -} diff --git a/auto_submit/lib/requests/graphql_queries.dart b/auto_submit/lib/requests/graphql_queries.dart deleted file mode 100644 index 0e7203dfa..000000000 --- a/auto_submit/lib/requests/graphql_queries.dart +++ /dev/null @@ -1,187 +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:gql/ast.dart'; -import 'package:gql/language.dart' as lang; - -/// Provides a way to encapsulate the named variables that will be needed for -/// each request made via the graphql api. -abstract class GraphQLOperation { - /// The list of variables that will be injected into the partner - /// [DocumentNode] made in the graphql service. - Map get variables; - - /// The document that contains the GraphQL operation. - DocumentNode get documentNode; -} - -/// [FindPullRequestsWithReviewsQuery] encapsulates the input variables and -/// [DocumentNode] needed to get a pull request with the last 30 reviews. -class FindPullRequestsWithReviewsQuery extends GraphQLOperation { - FindPullRequestsWithReviewsQuery({ - required this.repositoryOwner, - required this.repositoryName, - required this.pullRequestNumber, - }); - - final String repositoryOwner; - final String repositoryName; - final int pullRequestNumber; - - @override - Map get variables => { - 'sOwner': repositoryOwner, - 'sName': repositoryName, - 'sPrNumber': pullRequestNumber, - }; - - @override - DocumentNode get documentNode => lang.parseString(r''' -query LabeledPullRequestWithReviews($sOwner: String!, $sName: String!, $sPrNumber: Int!) { - repository(owner: $sOwner, name: $sName) { - pullRequest(number: $sPrNumber) { - author { - login - } - authorAssociation - id - title - mergeable - commits(last:1) { - nodes { - commit { - abbreviatedOid - oid - committedDate - pushedDate - status { - contexts { - context - state - targetUrl - } - } - } - } - } - reviews(last: 30, states: [APPROVED, CHANGES_REQUESTED]) { - nodes { - author { - login - } - authorAssociation - state - } - } - } - } -}'''); -} - -/// [FindPullRequestNodeIdQuery] encapsulate the input variables and -/// [DocumentNode] needed to the query the Pull Request Node ID that github uses -/// to locate a pull request accross all repos. -class FindPullRequestNodeIdQuery extends GraphQLOperation { - FindPullRequestNodeIdQuery({ - required this.repositoryOwner, - required this.repositoryName, - required this.pullRequestNumber, - }); - - final String repositoryOwner; - final String repositoryName; - final int pullRequestNumber; - - @override - Map get variables => { - 'repoOwner': repositoryOwner, - 'repoName': repositoryName, - 'pullRequestNumber': pullRequestNumber, - }; - - @override - DocumentNode get documentNode => lang.parseString(r''' -query FindPullRequestNodeId ($repoOwner:String!, $repoName:String!, $pullRequestNumber:Int!) { - repository(owner:$repoOwner, name:$repoName) { - pullRequest(number:$pullRequestNumber) { - id - } - } -} -'''); -} - -/// [RevertPullRequestMutation] encapsulates the input variables and -/// [DocumentNode] needed to perform the revert request mutation to revert a -/// closed pull request. -class RevertPullRequestMutation extends GraphQLOperation { - RevertPullRequestMutation( - this.body, - this.clientMutationId, - this.draft, - this.id, - this.title, - ); - - final String body; - final String? clientMutationId; - final bool draft; - final String id; - final String title; - - @override - Map get variables => { - 'revertBody': body, - 'clientMutationId': clientMutationId, - 'draft': draft, - 'pullRequestId': id, - 'revertTitle': title, - }; - - @override - DocumentNode get documentNode => lang.parseString(r''' -mutation RevertPullFlutterPullRequest ($revertBody:String!, $clientMutationId:String!, $draft:Boolean, $pullRequestId:ID!, $revertTitle:String!) { - revertPullRequest ( - input: { - body:$revertBody, - clientMutationId: $clientMutationId, - draft: $draft, - pullRequestId: $pullRequestId, - title: $revertTitle - }) { - clientMutationId - pullRequest { - author { - login - } - authorAssociation - id - title - number - repository { - owner { - login - } - name - } - } - revertPullRequest { - author { - login - } - authorAssociation - id - title - number - repository { - owner { - login - } - name - } - } - } -} -'''); -} diff --git a/auto_submit/lib/requests/readiness_check.dart b/auto_submit/lib/requests/readiness_check.dart deleted file mode 100644 index 428f9a6de..000000000 --- a/auto_submit/lib/requests/readiness_check.dart +++ /dev/null @@ -1,26 +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. - -import 'dart:async'; - -import 'package:shelf/shelf.dart'; - -import '../server/request_handler.dart'; - -/// Handler for readiness checks. -class ReadinessCheck extends RequestHandler { - const ReadinessCheck({ - required super.config, - }); - - @override - Future get() async { - return Response.ok('OK'); - } - - @override - Future run(Request request) async { - return super.run(request); - } -} diff --git a/auto_submit/lib/server/authenticated_request_handler.dart b/auto_submit/lib/server/authenticated_request_handler.dart deleted file mode 100644 index c62f9ad4a..000000000 --- a/auto_submit/lib/server/authenticated_request_handler.dart +++ /dev/null @@ -1,37 +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:meta/meta.dart'; -import 'package:shelf/shelf.dart'; - -import 'request_handler.dart'; -import '../request_handling/authentication.dart'; -import '../requests/exceptions.dart'; -import '../service/log.dart'; - -/// A [RequestHandler] that handles API requests. -/// -/// * All requests must be authenticated per [CronAuthProvider]. -@immutable -abstract class AuthenticatedRequestHandler extends RequestHandler { - /// Creates a new [ApiRequestHandler]. - const AuthenticatedRequestHandler({ - required super.config, - required this.cronAuthProvider, - }); - - /// Service responsible for authenticating this [Request]. - final CronAuthProvider cronAuthProvider; - - @override - Future run(Request request) async { - try { - await cronAuthProvider.authenticate(request); - } on Unauthenticated catch (error) { - log.info('Authenticate error: $error'); - return Response.forbidden(error.toString()); - } - return super.run(request); - } -} diff --git a/auto_submit/lib/server/request_handler.dart b/auto_submit/lib/server/request_handler.dart deleted file mode 100644 index 8d2499d39..000000000 --- a/auto_submit/lib/server/request_handler.dart +++ /dev/null @@ -1,57 +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 'package:shelf/shelf.dart'; - -import '../service/config.dart'; -import '../requests/exceptions.dart'; - -@immutable -abstract class RequestHandler { - const RequestHandler({ - required this.config, - }); - - final Config config; - - /// Services a request. - /// - /// The default implementation will respond with 405 method not allowed. - @protected - Future run(Request request) async { - try { - switch (request.method) { - case 'GET': - return await get() as Response; - case 'POST': - return await post(request) as Response; - default: - throw MethodNotAllowed(request.method); - } - } on HttpStatusException { - rethrow; - } - } - - /// Services a GET request. - /// - /// Subclasses should override this method if they support GET requests. - /// The default implementation will respond with 405 method not allowed. - @protected - Future get() async { - throw const MethodNotAllowed('GET'); - } - - /// Services a POST request. - /// - /// Subclasses should override this method if they support POST requests. - /// The default implementation will respond with 405 method not allowed. - @protected - Future post(Request request) async { - throw const MethodNotAllowed('POST'); - } -} diff --git a/auto_submit/lib/service/access_client_provider.dart b/auto_submit/lib/service/access_client_provider.dart deleted file mode 100644 index eb6046312..000000000 --- a/auto_submit/lib/service/access_client_provider.dart +++ /dev/null @@ -1,15 +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. - -import 'package:googleapis_auth/auth_io.dart'; -import 'package:http/http.dart'; - -class AccessClientProvider { - /// Returns an OAuth 2.0 authenticated access client for the device lab service account. - Future createAccessClient({ - List scopes = const ['https://www.googleapis.com/auth/cloud-platform'], - }) async { - return clientViaApplicationDefaultCredentials(scopes: scopes); - } -} diff --git a/auto_submit/lib/service/approver_service.dart b/auto_submit/lib/service/approver_service.dart deleted file mode 100644 index 22ba63764..000000000 --- a/auto_submit/lib/service/approver_service.dart +++ /dev/null @@ -1,69 +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. - -import 'package:auto_submit/service/log.dart'; -import 'package:github/github.dart' as github; - -import '../configuration/repository_configuration.dart'; -import '../service/config.dart'; - -/// Function signature for a [ApproverService] provider. -typedef ApproverServiceProvider = ApproverService Function(Config config); - -/// Provides github PR approval services. -class ApproverService { - const ApproverService(this.config); - - final Config config; - - /// Creates and returns a [ApproverService] using [config]. - static ApproverService defaultProvider(Config config) { - return ApproverService(config); - } - - /// Get the auto approval accounts from the configuration is any are supplied. - Future> getAutoApprovalAccounts(github.RepositorySlug slug) async { - final RepositoryConfiguration repositoryConfiguration = await config.getRepositoryConfiguration(slug); - final Set approvalAccounts = repositoryConfiguration.autoApprovalAccounts; - return approvalAccounts; - } - - Future autoApproval(github.PullRequest pullRequest) async { - final String? author = pullRequest.user!.login; - final int prNumber = pullRequest.number!; - final github.RepositorySlug slug = pullRequest.base!.repo!.slug(); - final Set approvalAccounts = - await getAutoApprovalAccounts(github.RepositorySlug.full(pullRequest.base!.repo!.fullName)); - - log.info('Determining auto approval of $author on ${slug.fullName}/$prNumber.'); - - // If there are auto_approvers let them approve the pull request. - if (!approvalAccounts.contains(author)) { - log.info('Auto-review ignored for $author on ${slug.fullName}/$prNumber.'); - } else { - log.info('Auto approval detected on ${slug.fullName}/$prNumber.'); - await _approve(pullRequest, author); - } - } - - Future _approve(github.PullRequest pullRequest, String? author) async { - final github.RepositorySlug slug = pullRequest.base!.repo!.slug(); - final github.GitHub botClient = await config.createFlutterGitHubBotClient(slug); - - final Stream reviews = botClient.pullRequests.listReviews(slug, pullRequest.number!); - // TODO(ricardoamador) this will need to be refactored to make this code more general and - // not applicable to only flutter. - await for (github.PullRequestReview review in reviews) { - if (review.user.login == 'fluttergithubbot' && review.state == 'APPROVED') { - // Already approved. - return; - } - } - - final github.CreatePullRequestReview review = - github.CreatePullRequestReview(slug.owner, slug.name, pullRequest.number!, 'APPROVE'); - await botClient.pullRequests.createReview(slug, review); - log.info('Review for ${slug.fullName}/${pullRequest.number} complete'); - } -} diff --git a/auto_submit/lib/service/bigquery.dart b/auto_submit/lib/service/bigquery.dart deleted file mode 100644 index a174b2bb4..000000000 --- a/auto_submit/lib/service/bigquery.dart +++ /dev/null @@ -1,410 +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. - -import 'dart:async'; - -import 'package:auto_submit/exception/bigquery_exception.dart'; -import 'package:auto_submit/model/big_query_pull_request_record.dart'; -import 'package:googleapis/bigquery/v2.dart'; -import 'package:http/http.dart'; - -import 'access_client_provider.dart'; - -const String selectRevertRequestDml = r''' -SELECT organization, - repository, - reverting_pr_author, - reverting_pr_number, - reverting_pr_commit, - reverting_pr_created_timestamp, - reverting_pr_landed_timestamp, - original_pr_author, - original_pr_number, - original_pr_commit, - original_pr_created_timestamp, - original_pr_landed_timestamp, - review_issue_assignee, - review_issue_number, - review_issue_created_timestamp, - review_issue_landed_timestamp, - review_issue_closed_by -FROM `flutter-dashboard.revert.revert_requests` -WHERE reverting_pr_number=@REVERTING_PR_NUMBER AND repository=@REPOSITORY -'''; - -/// Query to select all of the open revert review issues for update. Note that -/// the review_issue_landed_timestamp is 0 for open issues. 0 is the default -/// value instead of null since it is safer to process. -const String selectRevertRequestReviewIssuesDml = r''' -SELECT review_issue_assignee, - review_issue_number, - review_issue_created_timestamp, - review_issue_landed_timestamp, - review_issue_closed_by -FROM `flutter-dashboard.revert.revert_requests` -WHERE review_issue_landed_timestamp=0 -'''; - -const String updateRevertRequestRecordReviewDml = ''' -UPDATE `flutter-dashboard.revert.revert_requests` -SET review_issue_landed_timestamp=@REVIEW_ISSUE_LANDED_TIMESTAMP, - review_issue_closed_by=@REVIEW_ISSUE_CLOSED_BY -WHERE review_issue_number=@REVIEW_ISSUE_NUMBER -'''; - -const String insertRevertRequestDml = r''' -INSERT INTO `flutter-dashboard.revert.revert_requests` ( - organization, - repository, - reverting_pr_author, - reverting_pr_number, - reverting_pr_commit, - reverting_pr_created_timestamp, - reverting_pr_landed_timestamp, - original_pr_author, - original_pr_number, - original_pr_commit, - original_pr_created_timestamp, - original_pr_landed_timestamp, - review_issue_assignee, - review_issue_number, - review_issue_created_timestamp, - review_issue_landed_timestamp, - review_issue_closed_by -) VALUES ( - @ORGANIZATION, - @REPOSITORY, - @REVERTING_PR_AUTHOR, - @REVERTING_PR_NUMBER, - @REVERTING_PR_COMMIT, - @REVERTING_PR_CREATED_TIMESTAMP, - @REVERTING_PR_LANDED_TIMESTAMP, - @ORIGINAL_PR_AUTHOR, - @ORIGINAL_PR_NUMBER, - @ORIGINAL_PR_COMMIT, - @ORIGINAL_PR_CREATED_TIMESTAMP, - @ORIGINAL_PR_LANDED_TIMESTAMP, - @REVIEW_ISSUE_ASSIGNEE, - @REVIEW_ISSUE_NUMBER, - @REVIEW_ISSUE_CREATED_TIMESTAMP, - @REVIEW_ISSUE_LANDED_TIMESTAMP, - @REVIEW_ISSUE_CLOSED_BY -) -'''; - -const String deleteRevertRequestDml = r''' -DELETE FROM `flutter-dashboard.revert.revert_requests` -WHERE reverting_pr_number=@REVERTING_PR_NUMBER AND repository=@REPOSITORY -'''; - -const String insertPullRequestDml = r''' -INSERT INTO `flutter-dashboard.autosubmit.pull_requests` ( - pr_created_timestamp, - pr_landed_timestamp, - organization, - repository, - author, - pr_number, - pr_commit, - pr_request_type -) VALUES ( - @PR_CREATED_TIMESTAMP, - @PR_LANDED_TIMESTAMP, - @ORGANIZATION, - @REPOSITORY, - @AUTHOR, - @PR_NUMBER, - @PR_COMMIT, - @PR_REQUEST_TYPE -) -'''; - -const String selectPullRequestDml = r''' -SELECT pr_created_timestamp, - pr_landed_timestamp, - organization, - repository, - author, - pr_number, - pr_commit, - pr_request_type -FROM `flutter-dashboard.autosubmit.pull_requests` -WHERE pr_number=@PR_NUMBER AND repository=@REPOSITORY -'''; - -const String deletePullRequestDml = r''' -DELETE FROM `flutter-dashboard.autosubmit.pull_requests` -WHERE pr_number=@PR_NUMBER AND repository=@REPOSITORY -'''; - -class BigqueryService { - const BigqueryService(this.accessClientProvider); - - /// AccessClientProvider for OAuth 2.0 authenticated access client - final AccessClientProvider accessClientProvider; - - /// Return a [TabledataResource] with an authenticated [client] - Future defaultTabledata() async { - final Client client = await accessClientProvider.createAccessClient( - scopes: const [BigqueryApi.bigqueryScope], - ); - return BigqueryApi(client).tabledata; - } - - /// Return a [JobsResource] with an authenticated [client] - Future defaultJobs() async { - final Client client = await accessClientProvider.createAccessClient( - scopes: const [BigqueryApi.bigqueryScope], - ); - return BigqueryApi(client).jobs; - } - - /// Query the database to update a revert review issue with the timestamp the - /// issue was closed at and the person who closed the issue. - Future updateReviewRequestIssue({ - required String projectId, - required DateTime reviewIssueLandedTimestamp, - required int reviewIssueNumber, - required String reviewIssueClosedBy, - }) async { - final JobsResource jobsResource = await defaultJobs(); - - final QueryRequest queryRequest = QueryRequest( - query: updateRevertRequestRecordReviewDml, - queryParameters: [ - _createIntegerQueryParameter( - 'REVIEW_ISSUE_LANDED_TIMESTAMP', - reviewIssueLandedTimestamp.millisecondsSinceEpoch, - ), - _createIntegerQueryParameter( - 'REVIEW_ISSUE_NUMBER', - reviewIssueNumber, - ), - _createStringQueryParameter( - 'REVIEW_ISSUE_CLOSED_BY', - reviewIssueClosedBy, - ), - ], - useLegacySql: false, - ); - - final QueryResponse queryResponse = await jobsResource.query(queryRequest, projectId); - if (!queryResponse.jobComplete!) { - throw BigQueryException( - 'Update of review issue $reviewIssueNumber did not complete.', - ); - } - - if (queryResponse.numDmlAffectedRows != null && int.parse(queryResponse.numDmlAffectedRows!) != 1) { - throw BigQueryException( - 'There was an error updating revert request record review issue landed timestamp with review issue number $reviewIssueNumber.', - ); - } - } - - Future deleteRevertRequestRecord({ - required String projectId, - required int prNumber, - required String repository, - }) async { - final JobsResource jobsResource = await defaultJobs(); - - final QueryRequest queryRequest = QueryRequest( - query: deleteRevertRequestDml, - queryParameters: [ - _createIntegerQueryParameter('REVERTING_PR_NUMBER', prNumber), - _createStringQueryParameter('REPOSITORY', repository), - ], - useLegacySql: false, - ); - - final QueryResponse queryResponse = await jobsResource.query(queryRequest, projectId); - if (!queryResponse.jobComplete!) { - throw BigQueryException( - 'Delete revert request with pr# $prNumber in repository $repository did not complete.', - ); - } - - if (queryResponse.numDmlAffectedRows == null || int.parse(queryResponse.numDmlAffectedRows!) == 0) { - throw BigQueryException( - 'Could not find revert request with pr# $prNumber in repository $repository to delete.', - ); - } - - if (int.parse(queryResponse.numDmlAffectedRows!) != 1) { - throw BigQueryException( - 'More than one row was deleted from the database for revert request with pr# $prNumber in repository $repository.', - ); - } - } - - /// Insert a new pull request record into the database. - Future insertPullRequestRecord({ - required String projectId, - required PullRequestRecord pullRequestRecord, - }) async { - final JobsResource jobsResource = await defaultJobs(); - - final QueryRequest queryRequest = QueryRequest( - query: insertPullRequestDml, - queryParameters: [ - _createIntegerQueryParameter( - 'PR_CREATED_TIMESTAMP', - pullRequestRecord.prCreatedTimestamp!.millisecondsSinceEpoch, - ), - _createIntegerQueryParameter( - 'PR_LANDED_TIMESTAMP', - pullRequestRecord.prLandedTimestamp!.millisecondsSinceEpoch, - ), - _createStringQueryParameter( - 'ORGANIZATION', - pullRequestRecord.organization, - ), - _createStringQueryParameter( - 'REPOSITORY', - pullRequestRecord.repository, - ), - _createStringQueryParameter( - 'AUTHOR', - pullRequestRecord.author, - ), - _createIntegerQueryParameter( - 'PR_NUMBER', - pullRequestRecord.prNumber, - ), - _createStringQueryParameter( - 'PR_COMMIT', - pullRequestRecord.prCommit, - ), - _createStringQueryParameter( - 'PR_REQUEST_TYPE', - pullRequestRecord.prRequestType, - ), - ], - useLegacySql: false, - ); - - final QueryResponse queryResponse = await jobsResource.query(queryRequest, projectId); - if (!queryResponse.jobComplete!) { - throw BigQueryException( - 'Insert pull request $pullRequestRecord did not complete.', - ); - } - - if (queryResponse.numDmlAffectedRows != null && int.parse(queryResponse.numDmlAffectedRows!) != 1) { - throw BigQueryException( - 'There was an error inserting $pullRequestRecord into the table.', - ); - } - } - - /// Select a specific pull request form the database. - Future selectPullRequestRecordByPrNumber({ - required String projectId, - required int prNumber, - required String repository, - }) async { - final JobsResource jobsResource = await defaultJobs(); - - final QueryRequest queryRequest = QueryRequest( - query: selectPullRequestDml, - queryParameters: [ - _createIntegerQueryParameter('PR_NUMBER', prNumber), - _createStringQueryParameter('REPOSITORY', repository), - ], - useLegacySql: false, - ); - - final QueryResponse queryResponse = await jobsResource.query(queryRequest, projectId); - if (!queryResponse.jobComplete!) { - throw BigQueryException( - 'Get pull request by pr# $prNumber in repository $repository did not complete.', - ); - } - - final List? tableRows = queryResponse.rows; - if (tableRows == null || tableRows.isEmpty) { - throw BigQueryException( - 'Could not find an entry for pull request with pr# $prNumber in repository $repository.', - ); - } - - if (tableRows.length != 1) { - throw BigQueryException( - 'More than one record was returned for pull request with pr# $prNumber in repository $repository.', - ); - } - - final TableRow tableRow = tableRows.first; - - return PullRequestRecord( - prCreatedTimestamp: (tableRow.f![0].v != null) - ? DateTime.fromMillisecondsSinceEpoch(int.parse(tableRow.f![0].v as String)) - : null, - prLandedTimestamp: (tableRow.f![1].v != null) - ? DateTime.fromMillisecondsSinceEpoch(int.parse(tableRow.f![1].v as String)) - : null, - organization: tableRow.f![2].v as String, - repository: tableRow.f![3].v as String, - author: tableRow.f![4].v as String, - prNumber: int.parse(tableRow.f![5].v as String), - prCommit: tableRow.f![6].v as String, - prRequestType: tableRow.f![7].v as String, - ); - } - - Future deletePullRequestRecord({ - required String projectId, - required int prNumber, - required String repository, - }) async { - final JobsResource jobsResource = await defaultJobs(); - - final QueryRequest queryRequest = QueryRequest( - query: deletePullRequestDml, - queryParameters: [ - _createIntegerQueryParameter('PR_NUMBER', prNumber), - _createStringQueryParameter('REPOSITORY', repository), - ], - useLegacySql: false, - ); - - final QueryResponse queryResponse = await jobsResource.query(queryRequest, projectId); - if (!queryResponse.jobComplete!) { - throw BigQueryException( - 'Delete pull request with pr# $prNumber in repository $repository did not complete.', - ); - } - - if (queryResponse.numDmlAffectedRows == null || int.parse(queryResponse.numDmlAffectedRows!) == 0) { - throw BigQueryException( - 'Could not find pull request with pr# $prNumber in repository $repository to delete.', - ); - } - - if (int.parse(queryResponse.numDmlAffectedRows!) != 1) { - throw BigQueryException( - 'More than one row was deleted from the database for pull request with pr# $prNumber in repository $repository.', - ); - } - } - - /// Create an int parameter for query substitution. - QueryParameter _createIntegerQueryParameter(String name, int? value) { - return QueryParameter( - name: name, - parameterType: QueryParameterType(type: 'INT64'), - parameterValue: QueryParameterValue(value: value.toString()), - ); - } - - /// Create a String parameter for query substitution. - QueryParameter _createStringQueryParameter(String name, String? value) { - return QueryParameter( - name: name, - parameterType: QueryParameterType(type: 'STRING'), - parameterValue: QueryParameterValue(value: value), - ); - } -} diff --git a/auto_submit/lib/service/config.dart b/auto_submit/lib/service/config.dart deleted file mode 100644 index c1c0d9647..000000000 --- a/auto_submit/lib/service/config.dart +++ /dev/null @@ -1,284 +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. - -import 'dart:convert'; -import 'dart:typed_data'; - -import 'package:auto_submit/configuration/repository_configuration.dart'; -import 'package:auto_submit/configuration/repository_configuration_manager.dart'; -import 'package:corsac_jwt/corsac_jwt.dart'; -import 'package:github/github.dart'; -import 'package:googleapis/bigquery/v2.dart'; -import 'package:graphql/client.dart'; -import 'package:http/http.dart' as http; -import 'package:neat_cache/cache_provider.dart'; -import 'package:neat_cache/neat_cache.dart'; -import 'package:retry/retry.dart'; - -import '../foundation/providers.dart'; -import '../service/secrets.dart'; -import 'access_client_provider.dart'; -import 'bigquery.dart'; -import 'github_service.dart'; -import 'log.dart'; - -/// Configuration for the autosubmit engine. -class Config { - Config({ - required this.cacheProvider, - this.httpProvider = Providers.freshHttpClient, - required this.secretManager, - }) { - repositoryConfigurationManager = RepositoryConfigurationManager(this, cache); - } - - late RepositoryConfigurationManager repositoryConfigurationManager; - - /// Project/GCP constants - static const String flutter = 'flutter'; - static const String flutterGcpProjectId = 'flutter-dashboard'; - - // List of environment variable keys related to the Github app authentication. - static const String kGithubKey = 'AUTO_SUBMIT_GITHUB_KEY'; - static const String kGithubAppId = 'AUTO_SUBMIT_GITHUB_APP_ID'; - static const String kWebHookKey = 'AUTO_SUBMIT_WEBHOOK_TOKEN'; - static const String kFlutterGitHubBotKey = 'AUTO_SUBMIT_FLUTTER_GITHUB_TOKEN'; - - /// Labels autosubmit looks for on pull requests - static const String kAutosubmitLabel = 'autosubmit'; - - // Labels the bot looks for on revert requests. - // TODO (ricardoamador) https://github.com/flutter/flutter/issues/134845: - // add a link to a one page doc outlining the workflow that happens here. - - /// The `revert` label is used by developers to initiate the revert request. - /// This signals to the service that it should revert the changes in this pull - /// request. - static const String kRevertLabel = 'revert'; - - /// The `revert of` label is used exclusively by the bot. The user does not - /// add this. When the bot successfully pushes the revert request to Github - /// it adds this label to signify that it should then validate and merge this - /// as a revert. - static const String kRevertOfLabel = 'revert of'; - - /// The label which shows the overrideTree Status. - String get overrideTreeStatusLabel => 'warning: land on red to fix tree breakage'; - - /// Repository Slug data - /// GitHub repositories that use CI status to determine if pull requests can be submitted. - static Set reposWithTreeStatus = { - engineSlug, - flutterSlug, - }; - static RepositorySlug get engineSlug => RepositorySlug('flutter', 'engine'); - static RepositorySlug get flutterSlug => RepositorySlug('flutter', 'flutter'); - - String get autosubmitBot => 'auto-submit[bot]'; - - /// The names of autoroller accounts for the repositories. - /// - /// These accounts should not need reviews before merging. See - /// https://github.com/flutter/flutter/wiki/Autorollers - Set get rollerAccounts => const { - 'skia-flutter-autoroll', - 'engine-flutter-autoroll', - // REST API returns dependabot[bot] as author while GraphQL returns dependabot. We need - // both as we use graphQL to merge the PR and REST API to approve the PR. - 'dependabot[bot]', - 'dependabot', - 'DartDevtoolWorkflowBot', - }; - - /// Repository configuration variables - Duration get repositoryConfigurationTtl => const Duration(minutes: 10); - - /// PubSub configs - int get kPullMesssageBatchSize => 100; - - /// Number of Pub/Sub pull calls in each cron job run. - /// - /// TODO(keyonghan): monitor and optimize this number based on response time - /// https://github.com/flutter/cocoon/pull/2035/files#r938143840. - int get kPubsubPullNumber => 5; - - static String get pubsubTopicsPrefix => 'projects/$flutterGcpProjectId/topics'; - static String get pubsubSubscriptionsPrefix => 'projects/$flutterGcpProjectId/subscriptions'; - - String get pubsubPullRequestTopic => 'auto-submit-queue'; - String get pubsubPullRequestSubscription => 'auto-submit-queue-sub'; - - String get pubsubRevertRequestTopic => 'auto-submit-revert-queue'; - String get pubsubRevertRequestSubscription => 'auto-submit-revert-queue-sub'; - - /// Retry options for timing related retryable code. - static const RetryOptions mergeRetryOptions = RetryOptions( - delayFactor: Duration(milliseconds: 200), - maxDelay: Duration(seconds: 1), - maxAttempts: 5, - ); - - static const RetryOptions requiredChecksRetryOptions = RetryOptions( - delayFactor: Duration(milliseconds: 500), - maxDelay: Duration(seconds: 5), - maxAttempts: 5, - ); - - /// Pull request approval message - static const String pullRequestApprovalRequirementsMessage = - '- Merge guidelines: You need at least one approved review if you are already ' - 'part of flutter-hackers or two member reviews if you are not a flutter-hacker ' - 'before re-applying the autosubmit label. __Reviewers__: If you left a comment ' - 'approving, please use the "approve" review action instead.'; - - /// Config object members - final CacheProvider cacheProvider; - final HttpProvider httpProvider; - final SecretManager secretManager; - - Cache get cache => Cache(cacheProvider).withPrefix('config'); - - Future getRepositoryConfiguration(RepositorySlug slug) async { - return repositoryConfigurationManager.readRepositoryConfiguration(slug); - } - - Future createGithubService(RepositorySlug slug) async { - final GitHub github = await createGithubClient(slug); - return GithubService(github); - } - - Future createGithubClient(RepositorySlug slug) async { - final String token = await generateGithubToken(slug); - return GitHub(auth: Authentication.withToken(token)); - } - - Future createFlutterGitHubBotClient(RepositorySlug slug) async { - final String token = await getFlutterGitHubBotToken(); - return GitHub(auth: Authentication.withToken(token)); - } - - Future generateGithubToken(RepositorySlug slug) async { - // GitHub's secondary rate limits are run into very frequently when making auth tokens. - final Uint8List? cacheValue = await cache['githubToken-${slug.owner}'].get( - () => _generateGithubToken(slug), - // Tokens have a TTL of 10 minutes. AppEngine requests have a TTL of 1 minute. - // To ensure no expired tokens are used, set this to 10 - 1, with an extra buffer of a duplicate request. - const Duration(minutes: 8), - ) as Uint8List?; - return String.fromCharCodes(cacheValue!); - } - - Future getInstallationId(RepositorySlug slug) async { - final String jwt = await _generateGithubJwt(); - final Map headers = { - 'Authorization': 'Bearer $jwt', - 'Accept': 'application/vnd.github.machine-man-preview+json', - }; - // TODO(KristinBi): Upstream the github package.https://github.com/flutter/flutter/issues/100920 - final Uri githubInstallationUri = Uri.https('api.github.com', 'app/installations'); - final http.Client client = httpProvider(); - // TODO(KristinBi): Track the installation id by repo. https://github.com/flutter/flutter/issues/100808 - final http.Response response = await client.get( - githubInstallationUri, - headers: headers, - ); - final List> list = (json.decode(response.body) as List).cast>(); - late String installationId; - for (Map installData in list) { - if (installData['account']!['login']!.toString() == slug.owner) { - installationId = installData['id']!.toString(); - } - } - return installationId; - } - - Future createGitHubGraphQLClient(RepositorySlug slug) async { - final HttpLink httpLink = HttpLink( - 'https://api.github.com/graphql', - defaultHeaders: { - 'Accept': 'application/vnd.github.antiope-preview+json', - }, - ); - - final String token = await generateGithubToken(slug); - - final AuthLink authLink = AuthLink( - getToken: () async => 'Bearer $token', - ); - - return GraphQLClient( - cache: GraphQLCache(), - link: authLink.concat(httpLink), - ); - } - - Future createBigQueryService() async { - final AccessClientProvider accessClientProvider = AccessClientProvider(); - return BigqueryService(accessClientProvider); - } - - Future createTabledataResourceApi() async { - return (await createBigQueryService()).defaultTabledata(); - } - - Future _generateGithubToken(RepositorySlug slug) async { - final String jwt = await _generateGithubJwt(); - final Map headers = { - 'Authorization': 'Bearer $jwt', - 'Accept': 'application/vnd.github.machine-man-preview+json', - }; - final String installationId = await getInstallationId(slug); - final Uri githubAccessTokensUri = Uri.https('api.github.com', 'app/installations/$installationId/access_tokens'); - final http.Client client = httpProvider(); - final http.Response response = await client.post( - githubAccessTokensUri, - headers: headers, - ); - final Map jsonBody = jsonDecode(response.body) as Map; - if (jsonBody.containsKey('token') == false) { - log.warning(response.body); - throw Exception('generateGithubToken failed to get token from Github'); - } - final String token = jsonBody['token'] as String; - return Uint8List.fromList(token.codeUnits); - } - - Future _generateGithubJwt() async { - final String rawKey = await secretManager.get(kGithubKey); - final StringBuffer sb = StringBuffer(); - sb.writeln(rawKey.substring(0, 32)); - sb.writeln(rawKey.substring(32, rawKey.length - 30).replaceAll(' ', ' \n')); - sb.writeln(rawKey.substring(rawKey.length - 30, rawKey.length)); - final String privateKey = sb.toString(); - final JWTBuilder builder = JWTBuilder(); - final DateTime now = DateTime.now(); - builder - ..issuer = await secretManager.get(kGithubAppId) - ..issuedAt = now - ..expiresAt = now.add(const Duration(minutes: 10)); - final JWTRsaSha256Signer signer = JWTRsaSha256Signer(privateKey: privateKey); - final JWT signedToken = builder.getSignedToken(signer); - return signedToken.toString(); - } - - /// Get the webhook key - Future getWebhookKey() async { - final Uint8List? cacheValue = await cache[kWebHookKey].get( - () => _getValueFromSecretManager(kWebHookKey), - ) as Uint8List?; - return String.fromCharCodes(cacheValue!); - } - - Future getFlutterGitHubBotToken() async { - final Uint8List? cacheValue = await cache[kFlutterGitHubBotKey].get( - () => _getValueFromSecretManager(kFlutterGitHubBotKey), - ) as Uint8List?; - return String.fromCharCodes(cacheValue!); - } - - Future _getValueFromSecretManager(String key) async { - final String value = await secretManager.get(key); - return Uint8List.fromList(value.codeUnits); - } -} diff --git a/auto_submit/lib/service/github_service.dart b/auto_submit/lib/service/github_service.dart deleted file mode 100644 index ef2f5a68a..000000000 --- a/auto_submit/lib/service/github_service.dart +++ /dev/null @@ -1,271 +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. - -import 'dart:async'; -import 'dart:convert'; -import 'package:auto_submit/service/log.dart'; -import 'package:github/github.dart'; - -/// If a pull request was behind the tip of tree by _kBehindToT commits -/// then the bot tries to rebase it -const int _kBehindToT = 10; - -/// [GithubService] handles communication with the GitHub API. -class GithubService { - GithubService(this.github); - - final GitHub github; - - /// Retrieves check runs with the ref. - Future> getCheckRuns( - RepositorySlug slug, - String ref, - ) async { - return github.checks.checkRuns.listCheckRunsForRef(slug, ref: ref).toList(); - } - - Future> getCheckRunsFiltered({ - required RepositorySlug slug, - required String ref, - String? checkName, - CheckRunStatus? status, - CheckRunFilter? filter, - }) async { - return github.checks.checkRuns - .listCheckRunsForRef( - slug, - ref: ref, - checkName: checkName, - status: status, - filter: filter, - ) - .toList(); - } - - /// Fetches the specified commit. - Future getCommit(RepositorySlug slug, String sha) async { - return github.repositories.getCommit(slug, sha); - } - - Future> getPullRequestFiles(RepositorySlug slug, PullRequest pullRequest) async { - final int? pullRequestId = pullRequest.number; - final List listPullRequestFiles = []; - - if (pullRequestId == null) { - return listPullRequestFiles; - } - - final Stream pullRequestFiles = github.pullRequests.listFiles(slug, pullRequestId); - - await for (PullRequestFile file in pullRequestFiles) { - listPullRequestFiles.add(file); - } - - return listPullRequestFiles; - } - - /// Create a new issue in github. - Future createIssue({ - required RepositorySlug slug, - required String title, - required String body, - List? labels, - String? assignee, - List? assignees, - String? state, - }) async { - final IssueRequest issueRequest = IssueRequest( - title: title, - body: body, - labels: labels, - assignee: assignee, - assignees: assignees, - state: state, - ); - return github.issues.create(slug, issueRequest); - } - - Future getIssue({ - required RepositorySlug slug, - required int issueNumber, - }) async { - return github.issues.get(slug, issueNumber); - } - - /// Create a pull request. - Future createPullRequest({ - required RepositorySlug slug, - String? title, - String? head, - required String base, - bool draft = false, - String? body, - }) async { - final CreatePullRequest createPullRequest = CreatePullRequest(title, head, base, draft: draft, body: body); - return github.pullRequests.create(slug, createPullRequest); - } - - /// Fetches the specified pull request. - Future getPullRequest(RepositorySlug slug, int pullRequestNumber) async { - return github.pullRequests.get(slug, pullRequestNumber); - } - - Future> listPullRequests( - RepositorySlug slug, { - int? pages, - String? base, - String direction = 'desc', - String? head, - String sort = 'created', - String state = 'open', - }) async { - final List pullRequestsFound = []; - final Stream pullRequestStream = github.pullRequests.list( - slug, - pages: pages, - direction: direction, - head: head, - sort: sort, - state: state, - ); - await for (PullRequest pullRequest in pullRequestStream) { - pullRequestsFound.add(pullRequest); - } - return pullRequestsFound; - } - - Future addReviewersToPullRequest( - RepositorySlug slug, - int pullRequestNumber, - List reviewerLogins, - ) async { - final response = await github.request( - 'POST', - '/repos/${slug.fullName}/pulls/$pullRequestNumber/requested_reviewers', - body: GitHubJson.encode({'reviewers': reviewerLogins}), - ); - return response.statusCode == StatusCodes.CREATED; - } - - /// Compares two commits to fetch diff. - /// - /// The response will include details on the files that were changed between the two commits. - /// Relevant APIs: https://docs.github.com/en/rest/reference/commits#compare-two-commits - Future compareTwoCommits(RepositorySlug slug, String refBase, String refHead) async { - return github.repositories.compareCommits(slug, refBase, refHead); - } - - /// Removes a label from a pull request. - Future removeLabel(RepositorySlug slug, int issueNumber, String label) async { - return github.issues.removeLabelForIssue(slug, issueNumber, label); - } - - /// Add labels to a pull request. - Future> addLabels(RepositorySlug slug, int issueNumber, List labels) async { - return github.issues.addLabelsToIssue(slug, issueNumber, labels); - } - - /// Relevant API: https://docs.github.com/en/rest/issues/assignees?apiVersion=2022-11-28#add-assignees-to-an-issue - Future addAssignee(RepositorySlug slug, int number, List assignees) async { - final response = await github.request( - 'POST', - '/repos/${slug.fullName}/issues/$number/assignees', - body: GitHubJson.encode({'assignees': assignees}), - ); - return response.statusCode == StatusCodes.CREATED; - } - - /// Create a comment for a pull request. - Future createComment( - RepositorySlug slug, - int issueNumber, - String body, - ) async { - return github.issues.createComment(slug, issueNumber, body); - } - - /// Update a pull request branch - Future updateBranch(RepositorySlug slug, int number, String headSha) async { - final response = await github.request( - 'PUT', - '/repos/${slug.fullName}/pulls/$number/update-branch', - body: GitHubJson.encode({'expected_head_sha': headSha}), - ); - return response.statusCode == StatusCodes.ACCEPTED; - } - - Future getBranch(RepositorySlug slug, String branchName) async { - return github.repositories.getBranch(slug, branchName); - } - - Future deleteBranch(RepositorySlug slug, String branchName) async { - final String ref = 'heads/$branchName'; - return github.git.deleteReference(slug, ref); - } - - /// Merges a pull request according to the MergeMethod type. Current supported - /// merge method types are merge, rebase and squash. - Future mergePullRequest( - RepositorySlug slug, - int number, { - String? commitMessage, - MergeMethod mergeMethod = MergeMethod.merge, - String? requestSha, - }) async { - return github.pullRequests.merge( - slug, - number, - message: commitMessage, - mergeMethod: mergeMethod, - requestSha: requestSha, - ); - } - - /// Automerges a given pull request with HEAD to ensure the commit is not in conflicting state. - Future autoMergeBranch(PullRequest pullRequest) async { - final RepositorySlug slug = pullRequest.base!.repo!.slug(); - final int prNumber = pullRequest.number!; - final RepositoryCommit totCommit = await getCommit(slug, 'HEAD'); - final GitHubComparison comparison = await compareTwoCommits(slug, totCommit.sha!, pullRequest.base!.sha!); - if (comparison.behindBy! >= _kBehindToT) { - log.info('The current branch is behind by ${comparison.behindBy} commits.'); - final String headSha = pullRequest.head!.sha!; - await updateBranch(slug, prNumber, headSha); - } - } - - /// Get contents from a repository at the supplied path. - Future getFileContents(RepositorySlug slug, String path, {String? ref}) async { - final RepositoryContents repositoryContents = await github.repositories.getContents(slug, path, ref: ref); - if (!repositoryContents.isFile) { - throw 'Contents do not point to a file.'; - } - final String content = utf8.decode(base64.decode(repositoryContents.file!.content!.replaceAll('\n', ''))); - return content; - } - - /// Check to see if user is a member of team in org. - /// - /// Note that we catch here as the api returns a 404 if the user has no - /// membership in general or is not a member of the team. - Future isTeamMember(String team, String user, String org) async { - try { - final TeamMembershipState teamMembershipState = - await github.organizations.getTeamMembershipByName(org, team, user); - return teamMembershipState.isActive; - } on GitHubError { - return false; - } - } - - /// Get the definition of a single repository - Future getRepository(RepositorySlug slug) async { - return github.repositories.getRepository(slug); - } - - Future getDefaultBranch(RepositorySlug slug) async { - final Repository repository = await getRepository(slug); - return repository.defaultBranch; - } -} diff --git a/auto_submit/lib/service/graphql_service.dart b/auto_submit/lib/service/graphql_service.dart deleted file mode 100644 index d51fb54af..000000000 --- a/auto_submit/lib/service/graphql_service.dart +++ /dev/null @@ -1,53 +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. - -import 'package:auto_submit/service/log.dart'; -import 'package:gql/ast.dart'; -import 'package:graphql/client.dart'; - -import '../requests/exceptions.dart'; - -/// Service class used to execute GraphQL queries. -class GraphQlService { - /// Runs a GraphQL query using [slug], [prNumber] and a [GraphQL] client. - Future> queryGraphQL({ - required DocumentNode documentNode, - required Map variables, - required GraphQLClient client, - }) async { - final QueryResult queryResult = await client.query( - QueryOptions( - document: documentNode, - fetchPolicy: FetchPolicy.noCache, - variables: variables, - ), - ); - - if (queryResult.hasException) { - log.severe(queryResult.exception.toString()); - throw const BadRequestException('GraphQL query failed'); - } - return queryResult.data!; - } - - Future> mutateGraphQL({ - required DocumentNode documentNode, - required Map variables, - required GraphQLClient client, - }) async { - final QueryResult queryResult = await client.mutate( - MutationOptions( - document: documentNode, - fetchPolicy: FetchPolicy.noCache, - variables: variables, - ), - ); - - if (queryResult.hasException) { - log.severe(queryResult.exception.toString()); - throw const BadRequestException('GraphQL mutate failed'); - } - return queryResult.data!; - } -} diff --git a/auto_submit/lib/service/log.dart b/auto_submit/lib/service/log.dart deleted file mode 100644 index 2b07c9d5a..000000000 --- a/auto_submit/lib/service/log.dart +++ /dev/null @@ -1,7 +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. - -import 'package:logging/logging.dart'; - -final Logger log = Logger('auto_submit'); diff --git a/auto_submit/lib/service/process_method.dart b/auto_submit/lib/service/process_method.dart deleted file mode 100644 index 3a7eea63f..000000000 --- a/auto_submit/lib/service/process_method.dart +++ /dev/null @@ -1,11 +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. - -/// Enum to tell the auto-submit bot which action to take based on the label -/// found. -enum ProcessMethod { - processAutosubmit, - processRevert, - doNotProcess, -} diff --git a/auto_submit/lib/service/pull_request_validation_service.dart b/auto_submit/lib/service/pull_request_validation_service.dart deleted file mode 100644 index 3dcd8dd45..000000000 --- a/auto_submit/lib/service/pull_request_validation_service.dart +++ /dev/null @@ -1,145 +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:auto_submit/configuration/repository_configuration.dart'; -import 'package:auto_submit/model/auto_submit_query_result.dart'; -import 'package:auto_submit/model/pull_request_data_types.dart'; -import 'package:auto_submit/request_handling/pubsub.dart'; -import 'package:auto_submit/service/approver_service.dart'; -import 'package:auto_submit/service/validation_service.dart'; -import 'package:auto_submit/service/config.dart'; -import 'package:auto_submit/service/github_service.dart'; -import 'package:auto_submit/service/log.dart'; -import 'package:auto_submit/service/process_method.dart'; -import 'package:auto_submit/validations/validation.dart'; -import 'package:auto_submit/validations/validation_filter.dart'; -import 'package:github/github.dart' as github; -import 'package:retry/retry.dart'; - -class PullRequestValidationService extends ValidationService { - PullRequestValidationService(Config config, {RetryOptions? retryOptions}) - : super(config, retryOptions: retryOptions) { - /// Validates a PR marked with the reverts label. - approverService = ApproverService(config); - } - - ApproverService? approverService; - - /// Processes a pub/sub message associated with PullRequest event. - Future processMessage(github.PullRequest messagePullRequest, String ackId, PubSub pubsub) async { - if (await shouldProcess(messagePullRequest)) { - await processPullRequest( - config: config, - result: await getNewestPullRequestInfo(config, messagePullRequest), - messagePullRequest: messagePullRequest, - ackId: ackId, - pubsub: pubsub, - ); - } else { - log.info('Should not process ${messagePullRequest.toJson()}, and ack the message.'); - await pubsub.acknowledge('auto-submit-queue-sub', ackId); - } - } - - Future shouldProcess(github.PullRequest pullRequest) async { - final (currentPullRequest, labelNames) = await getPrWithLabels(pullRequest); - return (currentPullRequest.state == 'open' && labelNames.contains(Config.kAutosubmitLabel)); - } - - /// Processes a PullRequest running several validations to decide whether to - /// land the commit or remove the autosubmit label. - Future processPullRequest({ - required Config config, - required QueryResult result, - required github.PullRequest messagePullRequest, - required String ackId, - required PubSub pubsub, - }) async { - final github.RepositorySlug slug = messagePullRequest.base!.repo!.slug(); - final int prNumber = messagePullRequest.number!; - final RepositoryConfiguration repositoryConfiguration = await config.getRepositoryConfiguration(slug); - - // filter out validations here - final ValidationFilter validationFilter = ValidationFilter( - config: config, - processMethod: ProcessMethod.processAutosubmit, - repositoryConfiguration: repositoryConfiguration, - ); - final Set validations = validationFilter.getValidations(); - - final Map validationsMap = {}; - final GithubService githubService = await config.createGithubService(slug); - - // get the labels before validation so that we can detect all labels. - // TODO (https://github.com/flutter/flutter/issues/132811) remove this after graphql is removed. - final github.PullRequest updatedPullRequest = await githubService.getPullRequest(slug, messagePullRequest.number!); - - /// Runs all the validation defined in the service. - /// If the runCi flag is false then we need a way to not run the ciSuccessful validation. - for (Validation validation in validations) { - log.info('${slug.fullName}/$prNumber running validation ${validation.name}'); - final ValidationResult validationResult = await validation.validate( - result, - updatedPullRequest, - ); - validationsMap[validation.name] = validationResult; - } - - /// If there is at least one action that requires to remove label do so and add comments for all the failures. - bool shouldReturn = false; - for (MapEntry result in validationsMap.entries) { - if (!result.value.result && result.value.action == Action.REMOVE_LABEL) { - final String commmentMessage = result.value.message.isEmpty ? 'Validations Fail.' : result.value.message; - final String message = 'auto label is removed for ${slug.fullName}/$prNumber, due to $commmentMessage'; - await githubService.removeLabel(slug, prNumber, Config.kAutosubmitLabel); - await githubService.createComment(slug, prNumber, message); - log.info(message); - shouldReturn = true; - } - } - - if (shouldReturn) { - log.info( - 'The pr ${slug.fullName}/$prNumber with message: $ackId should be acknowledged due to validation failure.', - ); - await pubsub.acknowledge('auto-submit-queue-sub', ackId); - log.info('The pr ${slug.fullName}/$prNumber is not feasible for merge and message: $ackId is acknowledged.'); - return; - } - - // If PR has some failures to ignore temporarily do nothing and continue. - for (MapEntry result in validationsMap.entries) { - if (!result.value.result && result.value.action == Action.IGNORE_TEMPORARILY) { - log.info( - 'Temporarily ignoring processing of ${slug.fullName}/$prNumber due to ${result.key} failing validation.', - ); - return; - } - } - - // If we got to this point it means we are ready to submit the PR. - final MergeResult processed = await processMerge( - config: config, - messagePullRequest: messagePullRequest, - ); - - if (!processed.result) { - final String message = 'auto label is removed for ${slug.fullName}/$prNumber, ${processed.message}.'; - await githubService.removeLabel(slug, prNumber, Config.kAutosubmitLabel); - await githubService.createComment(slug, prNumber, message); - log.info(message); - } else { - log.info('Pull Request ${slug.fullName}/$prNumber was merged successfully!'); - log.info('Attempting to insert a pull request record into the database for $prNumber'); - await insertPullRequestRecord( - config: config, - pullRequest: messagePullRequest, - pullRequestType: PullRequestChangeType.change, - ); - } - - log.info('Ack the processed message : $ackId.'); - await pubsub.acknowledge('auto-submit-queue-sub', ackId); - } -} diff --git a/auto_submit/lib/service/revert_issue_body_formatter.dart b/auto_submit/lib/service/revert_issue_body_formatter.dart deleted file mode 100644 index c41e5a43c..000000000 --- a/auto_submit/lib/service/revert_issue_body_formatter.dart +++ /dev/null @@ -1,53 +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:github/github.dart'; - -class RevertIssueBodyFormatter { - RevertIssueBodyFormatter({ - required this.slug, - required this.originalPrNumber, - required this.initiatingAuthor, - required this.originalPrTitle, - required this.originalPrBody, - }); - - // Possible format for the revert issue - RepositorySlug slug; - String initiatingAuthor; - int originalPrNumber; - // These two fields can be null in the original pull request. - String? originalPrTitle; - String? originalPrBody; - - late String? revertPrTitle; - late String? revertPrBody; - late String? revertPrLink; - - RevertIssueBodyFormatter get format { - // Create the title for the revert issue. - originalPrTitle ??= 'No title provided.'; - revertPrTitle = 'Reverts "$originalPrTitle"'; - - // create the reverts Link for the body. Looks like Reverts flutter/cocoon#123 but will render as a link. - revertPrLink = 'Reverts ${slug.fullName}#$originalPrNumber'; - - originalPrBody ??= 'No description provided.'; - - // Create the body for the revert issue. - revertPrBody = ''' -$revertPrLink -Initiated by: $initiatingAuthor -This change reverts the following previous change: -Original Description: -$originalPrBody -'''; - - return this; - } - - String? get formattedRevertPrTitle => revertPrTitle; - - String? get formattedRevertPrBody => revertPrBody; -} diff --git a/auto_submit/lib/service/revert_request_validation_service.dart b/auto_submit/lib/service/revert_request_validation_service.dart deleted file mode 100644 index e1fe761e5..000000000 --- a/auto_submit/lib/service/revert_request_validation_service.dart +++ /dev/null @@ -1,263 +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:auto_submit/action/git_cli_revert_method.dart'; -import 'package:auto_submit/configuration/repository_configuration.dart'; -import 'package:auto_submit/model/auto_submit_query_result.dart'; -import 'package:auto_submit/model/pull_request_data_types.dart'; -import 'package:auto_submit/request_handling/pubsub.dart'; -import 'package:auto_submit/requests/github_pull_request_event.dart'; -import 'package:auto_submit/service/approver_service.dart'; -import 'package:auto_submit/service/validation_service.dart'; -import 'package:auto_submit/service/config.dart'; -import 'package:auto_submit/service/github_service.dart'; -import 'package:auto_submit/service/log.dart'; -import 'package:auto_submit/validations/validation.dart'; -import 'package:auto_submit/validations/validation_filter.dart'; -import 'package:github/github.dart' as github; -import 'package:meta/meta.dart'; -import 'package:retry/retry.dart'; -import 'package:auto_submit/action/revert_method.dart'; -import 'process_method.dart'; - -enum RevertProcessMethod { revert, revertOf, none } - -class RevertRequestValidationService extends ValidationService { - RevertRequestValidationService(Config config, {RetryOptions? retryOptions, RevertMethod? revertMethod}) - : revertMethod = revertMethod ?? GitCliRevertMethod(), - super(config, retryOptions: retryOptions) { - /// Validates a PR marked with the reverts label. - approverService = ApproverService(config); - } - - ApproverService? approverService; - @visibleForTesting - RevertMethod? revertMethod; - @visibleForTesting - ValidationFilter? validationFilter; - - /// TODO run the actual request from here and remove the shouldProcess call. - /// Processes a pub/sub message associated with PullRequest event. - Future processMessage(GithubPullRequestEvent githubPullRequestEvent, String ackId, PubSub pubsub) async { - // Make sure the pull request still contains the labels. - final github.PullRequest messagePullRequest = githubPullRequestEvent.pullRequest!; - final (currentPullRequest, labelNames) = await getPrWithLabels(messagePullRequest); - final RevertProcessMethod revertProcessMethod = await shouldProcess(currentPullRequest, labelNames); - - final GithubPullRequestEvent updatedGithubPullRequestEvent = GithubPullRequestEvent( - pullRequest: currentPullRequest, - action: githubPullRequestEvent.action, - sender: githubPullRequestEvent.sender, - ); - - switch (revertProcessMethod) { - // Revert is the processing of the closed issue. - case RevertProcessMethod.revert: - await processRevertRequest( - result: await getNewestPullRequestInfo(config, messagePullRequest), - githubPullRequestEvent: updatedGithubPullRequestEvent, - ackId: ackId, - pubsub: pubsub, - ); - break; - // Reverts is the processing of the opened revert issue. - case RevertProcessMethod.revertOf: - await processRevertOfRequest( - result: await getNewestPullRequestInfo(config, messagePullRequest), - githubPullRequestEvent: updatedGithubPullRequestEvent, - ackId: ackId, - pubsub: pubsub, - ); - break; - // Do not process. - case RevertProcessMethod.none: - log.info('Should not process ${messagePullRequest.toJson()}, and ack the message.'); - await pubsub.acknowledge(config.pubsubRevertRequestSubscription, ackId); - break; - } - } - - /// Check whether the original request is within the 24 hour time limit to revert. - bool isWithinTimeLimit(github.PullRequest pullRequest) { - if (pullRequest.mergedAt == null) { - // This pull request has never been merged. - return false; - } - return DateTime.now().difference(pullRequest.mergedAt!).inHours <= 24; - } - - /// Determine if we should process the incoming pull request webhook event. - Future shouldProcess(github.PullRequest pullRequest, List labelNames) async { - // This is the initial revert request state. - if (pullRequest.state == 'closed' && labelNames.contains(Config.kRevertLabel) && pullRequest.mergedAt != null) { - return RevertProcessMethod.revert; - } else if (pullRequest.state == 'open' && - labelNames.contains(Config.kRevertOfLabel) && - pullRequest.user!.login == 'auto-submit[bot]') { - // This is the path where we check validations - return RevertProcessMethod.revertOf; - } - return RevertProcessMethod.none; - } - - // pullRequest.state == 'closed' && labelNames.contains('revert') - // TODO need a way to stop processing this. - Future processRevertRequest({ - required QueryResult result, - required GithubPullRequestEvent githubPullRequestEvent, - required String ackId, - required PubSub pubsub, - }) async { - final github.PullRequest messagePullRequest = githubPullRequestEvent.pullRequest!; - final github.RepositorySlug slug = messagePullRequest.base!.repo!.slug(); - final GithubService githubService = await config.createGithubService(slug); - final String sender = githubPullRequestEvent.sender!.login!; - - if (!isWithinTimeLimit(messagePullRequest)) { - final String message = '''Time to revert pull request ${slug.fullName}/${messagePullRequest.number} has elapsed. - You need to open the revert manually and process as a regular pull request.'''; - log.info(message); - await githubService.createComment(slug, messagePullRequest.number!, message); - await githubService.removeLabel(slug, messagePullRequest.number!, Config.kRevertLabel); - log.info('Should not process ${messagePullRequest.toJson()}, and ack the message.'); - await pubsub.acknowledge(config.pubsubRevertRequestSubscription, ackId); - return; - } - - // Attempt to create the new revert pull request. - try { - // This is the autosubmit query result pull request from graphql. - final github.PullRequest pullRequest = - await revertMethod!.createRevert(config, sender, messagePullRequest) as github.PullRequest; - log.info('Created revert pull request ${slug.fullName}/${pullRequest.number}.'); - // This will come through this service again for processing. - await githubService.addLabels(slug, pullRequest.number!, [Config.kRevertOfLabel]); - log.info('Assigning new revert issue to $sender'); - await githubService.addAssignee(slug, pullRequest.number!, [sender]); - // TODO (ricardoamador) create a better solution than this to stop processing - // the revert requests. Maybe change the label after the revert has occurred. - // For some reason we get duplicate events even though we ack the message. - await githubService.removeLabel(slug, messagePullRequest.number!, Config.kRevertLabel); - // Notify the discord tree channel that the revert issue has been created - // and will be processed. - } catch (e) { - final String message = 'Unable to create the revert pull request due to ${e.toString()}'; - log.severe(message); - await githubService.createComment(slug, messagePullRequest.number!, message); - await githubService.removeLabel(slug, messagePullRequest.number!, Config.kRevertLabel); - } finally { - await pubsub.acknowledge(config.pubsubRevertRequestSubscription, ackId); - } - } - - /// Processes a PullRequest running several validations to decide whether to - /// land the commit or remove the label. - // pullRequest.state == 'open' && labelNames.contains('revert of') - Future processRevertOfRequest({ - required QueryResult result, - required GithubPullRequestEvent githubPullRequestEvent, - required String ackId, - required PubSub pubsub, - }) async { - final github.PullRequest messagePullRequest = githubPullRequestEvent.pullRequest!; - final github.RepositorySlug slug = messagePullRequest.base!.repo!.slug(); - final GithubService githubService = await config.createGithubService(slug); - final int prNumber = messagePullRequest.number!; - - // Check to make sure the repository allows review-less revert pull requests - // so that we can reassign if needed otherwise autoapprove the pull request. - final RepositoryConfiguration repositoryConfiguration = await config.getRepositoryConfiguration(slug); - if (!repositoryConfiguration.supportNoReviewReverts) { - await githubService.removeLabel(slug, prNumber, Config.kRevertOfLabel); - await githubService.createComment( - slug, - prNumber, - 'Repository configuration does not support review-less revert pull requests. Please assign at least two reviewers to this pull request.', - ); - // We do not want to continue processing this issue. - log.info('Ack the processed message : $ackId.'); - await pubsub.acknowledge(config.pubsubRevertRequestSubscription, ackId); - return; - } - - validationFilter ??= ValidationFilter( - config: config, - processMethod: ProcessMethod.processRevert, - repositoryConfiguration: repositoryConfiguration, - ); - - final Set validations = validationFilter!.getValidations(); - - final Map validationsMap = {}; - - /// Runs all the validation defined in the service. - /// If the runCi flag is false then we need a way to not run the ciSuccessful validation. - for (Validation validation in validations) { - log.info('${slug.fullName}/$prNumber running validation ${validation.name}'); - validationsMap[validation.name] = await validation.validate( - result, - // this needs to be the newly opened pull request. - messagePullRequest, - ); - } - - /// If there is at least one action that requires to remove label do so and add comments for all the failures. - bool shouldReturn = false; - for (MapEntry result in validationsMap.entries) { - if (!result.value.result && result.value.action == Action.REMOVE_LABEL) { - final String commmentMessage = result.value.message.isEmpty ? 'Validations Fail.' : result.value.message; - final String message = 'auto label is removed for ${slug.fullName}/$prNumber, due to $commmentMessage'; - await githubService.removeLabel(slug, prNumber, Config.kRevertOfLabel); - await githubService.createComment(slug, prNumber, message); - log.info(message); - shouldReturn = true; - } - } - - if (shouldReturn) { - log.info( - 'The pr ${slug.fullName}/$prNumber with message: $ackId should be acknowledged due to validation failure.', - ); - log.info('The pr ${slug.fullName}/$prNumber is not feasible for merge and message: $ackId is acknowledged.'); - await pubsub.acknowledge(config.pubsubRevertRequestSubscription, ackId); - return; - } - - // If PR has some failures to ignore temporarily do nothing and continue. - for (MapEntry result in validationsMap.entries) { - if (!result.value.result && result.value.action == Action.IGNORE_TEMPORARILY) { - log.info( - 'Temporarily ignoring processing of ${slug.fullName}/$prNumber due to ${result.key} failing validation.', - ); - return; - } - } - - // If we got to this point it means we are ready to submit the PR. - final MergeResult processed = await processMerge( - config: config, - messagePullRequest: messagePullRequest, - ); - - if (!processed.result) { - final String message = 'auto label is removed for ${slug.fullName}/$prNumber, ${processed.message}.'; - await githubService.removeLabel(slug, prNumber, Config.kRevertOfLabel); - await githubService.createComment(slug, prNumber, message); - log.info(message); - } else { - log.info('Revert merged successfully, deleting branch ${messagePullRequest.head!.ref!}'); - await githubService.deleteBranch(slug, messagePullRequest.head!.ref!); - log.info('Pull Request ${slug.fullName}/$prNumber was merged successfully!'); - log.info('Attempting to insert a pull request record into the database for $prNumber'); - await insertPullRequestRecord( - config: config, - pullRequest: messagePullRequest, - pullRequestType: PullRequestChangeType.revert, - ); - } - - log.info('Ack the processed message : $ackId.'); - await pubsub.acknowledge(config.pubsubRevertRequestSubscription, ackId); - } -} diff --git a/auto_submit/lib/service/secrets.dart b/auto_submit/lib/service/secrets.dart deleted file mode 100644 index a1e33e77a..000000000 --- a/auto_submit/lib/service/secrets.dart +++ /dev/null @@ -1,78 +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. - -import 'dart:convert'; -import 'dart:io'; - -import 'package:auto_submit/service/config.dart'; -import 'package:appengine/appengine.dart'; -import 'package:googleapis/secretmanager/v1.dart'; - -/// Access secrets for Google Cloud projects. -/// -/// See also: -/// * https://cloud.google.com/secret-manager/docs -abstract class SecretManager { - const SecretManager(); - - Future get(String key); - - void put(String key, [String? value]); -} - -class CloudSecretManager implements SecretManager { - CloudSecretManager(); - - final String projectId = Platform.environment['APPLICATION_ID'] ?? Config.flutterGcpProjectId; - - @override - Future get( - String key, { - String? fields, - }) async { - final SecretManagerApi api = SecretManagerApi(authClientService); - final SecretPayload? payload = (await api.projects.secrets.versions.access( - 'projects/$projectId/secrets/$key/versions/latest', - $fields: fields, - )) - .payload; - if (payload?.data == null) { - throw SecretManagerException('Failed to find secret for $key with \$fields=$fields'); - } - return String.fromCharCodes(base64Decode(payload!.data!)); - } - - @override - void put(String key, [String? value]) => throw UnimplementedError('put is only supported for local runs'); -} - -/// Local instance of [SecretManager] for use in testing. -class LocalSecretManager implements SecretManager { - final Map _secrets = {}; - - @override - Future get(String key, {String? fields}) async { - if (_secrets.containsKey(key)) { - return _secrets[key]!; - } else if (Platform.environment.containsKey(key)) { - return Platform.environment[key]!; - } - - throw Exception('Failed to find $key in environment. Try adding it to the environment variables'); - } - - @override - void put(String key, [String? value]) { - _secrets[key] = value; - } -} - -class SecretManagerException implements Exception { - SecretManagerException([this.message]); - - final String? message; - - @override - String toString() => 'SecretManagerException: $message'; -} diff --git a/auto_submit/lib/service/validation_service.dart b/auto_submit/lib/service/validation_service.dart deleted file mode 100644 index 05670021d..000000000 --- a/auto_submit/lib/service/validation_service.dart +++ /dev/null @@ -1,223 +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:auto_submit/exception/bigquery_exception.dart'; -import 'package:auto_submit/exception/retryable_exception.dart'; -import 'package:auto_submit/model/auto_submit_query_result.dart'; -import 'package:auto_submit/model/big_query_pull_request_record.dart'; -import 'package:auto_submit/model/pull_request_data_types.dart'; -import 'package:auto_submit/requests/graphql_queries.dart'; -import 'package:auto_submit/service/bigquery.dart'; -import 'package:auto_submit/service/config.dart'; -import 'package:auto_submit/service/github_service.dart'; -import 'package:auto_submit/service/graphql_service.dart'; -import 'package:auto_submit/service/log.dart'; -import 'package:github/github.dart' as github; -import 'package:graphql/client.dart' as graphql; -import 'package:retry/retry.dart'; - -/// Class containing common methods to each of the pull request type validation -/// services. -class ValidationService { - ValidationService(this.config, {RetryOptions? retryOptions}) - : retryOptions = retryOptions ?? Config.mergeRetryOptions; - - final Config config; - final RetryOptions retryOptions; - - /// Fetch the most up to date info for the current pull request from github. - Future getNewestPullRequestInfo(Config config, github.PullRequest pullRequest) async { - final github.RepositorySlug slug = pullRequest.base!.repo!.slug(); - final graphql.GraphQLClient graphQLClient = await config.createGitHubGraphQLClient(slug); - final int? prNumber = pullRequest.number; - final GraphQlService graphQlService = GraphQlService(); - - final FindPullRequestsWithReviewsQuery findPullRequestsWithReviewsQuery = FindPullRequestsWithReviewsQuery( - repositoryOwner: slug.owner, - repositoryName: slug.name, - pullRequestNumber: prNumber!, - ); - - final Map data = await graphQlService.queryGraphQL( - documentNode: findPullRequestsWithReviewsQuery.documentNode, - variables: findPullRequestsWithReviewsQuery.variables, - client: graphQLClient, - ); - - return QueryResult.fromJson(data); - } - - Future<(github.PullRequest, List)> getPrWithLabels(github.PullRequest pullRequest) async { - final github.RepositorySlug slug = pullRequest.base!.repo!.slug(); - final GithubService githubService = await config.createGithubService(slug); - final github.PullRequest currentPullRequest = await githubService.getPullRequest(slug, pullRequest.number!); - final List labelNames = (currentPullRequest.labels as List) - .map((github.IssueLabel labelMap) => labelMap.name) - .toList(); - return (currentPullRequest, labelNames); - } - - /// Merges the commit if the PullRequest passes all the validations. - Future processMerge({ - required Config config, - required github.PullRequest messagePullRequest, - }) async { - final github.RepositorySlug slug = messagePullRequest.base!.repo!.slug(); - final int number = messagePullRequest.number!; - - // Pass an explicit commit message from the PR title otherwise the GitHub API will use the first commit message. - const String revertPattern = 'Revert "Revert'; - String messagePrefix = ''; - - if (messagePullRequest.title!.contains(revertPattern)) { - // Cleanup auto-generated revert messages. - messagePrefix = ''' -${messagePullRequest.title!.replaceFirst('Revert "Revert', 'Reland')} - -'''; - } - - final String prBody = _sanitizePrBody(messagePullRequest.body ?? ''); - final String commitMessage = '$messagePrefix$prBody'; - - try { - github.PullRequestMerge? result; - - await retryOptions.retry( - () async { - result = await _processMergeInternal( - config: config, - commitMessage: commitMessage, - slug: slug, - number: number, - // TODO(ricardoamador): make this configurable per repository, https://github.com/flutter/flutter/issues/114557 - mergeMethod: github.MergeMethod.squash, - ); - }, - retryIf: (Exception e) => e is RetryableException, - ); - - final bool merged = result?.merged ?? false; - if (result != null && !merged) { - final String message = 'Failed to merge ${slug.fullName}/$number with ${result?.message}'; - log.severe(message); - return (result: false, message: message); - } - } catch (e) { - // Catch graphql client init exceptions. - final String message = 'Failed to merge ${slug.fullName}/$number with ${e.toString()}'; - log.severe(message); - return (result: false, message: message); - } - - return (result: true, message: commitMessage); - } - - /// Insert a merged pull request record into the database. - Future insertPullRequestRecord({ - required Config config, - required github.PullRequest pullRequest, - required PullRequestChangeType pullRequestType, - }) async { - final github.RepositorySlug slug = pullRequest.base!.repo!.slug(); - final GithubService gitHubService = await config.createGithubService(slug); - // We need the updated time fields for the merged request from github. - final github.PullRequest currentPullRequest = await gitHubService.getPullRequest(slug, pullRequest.number!); - - log.info('Updated pull request info for ${slug.fullName}/${pullRequest.number}'); - - // add a record for the pull request into our metrics tracking - final PullRequestRecord pullRequestRecord = PullRequestRecord( - organization: currentPullRequest.base!.repo!.slug().owner, - repository: currentPullRequest.base!.repo!.slug().name, - author: currentPullRequest.user!.login, - prNumber: pullRequest.number!, - prCommit: currentPullRequest.head!.sha, - prRequestType: pullRequestType.name, - prCreatedTimestamp: currentPullRequest.createdAt!, - prLandedTimestamp: currentPullRequest.closedAt!, - ); - - log.info('Created pull request record: ${pullRequestRecord.toString()}'); - - try { - final BigqueryService bigqueryService = await config.createBigQueryService(); - await bigqueryService.insertPullRequestRecord( - projectId: Config.flutterGcpProjectId, - pullRequestRecord: pullRequestRecord, - ); - log.info('Record inserted for pull request ${slug.fullName}/${pullRequest.number} successfully.'); - } on BigQueryException catch (exception) { - log.severe( - 'Unable to insert pull request record for pull request ${slug.fullName}/${pullRequest.number} due to: ${exception.toString()}', - ); - } - } -} - -/// Small wrapper class to allow us to capture and create a comment in the PR with -/// the issue that caused the merge failure. -typedef MergeResult = ({bool result, String message}); - -/// Function signature that will be executed with retries. -typedef RetryHandler = Function(); - -/// Internal wrapper for the logic of merging a pull request into github. -Future _processMergeInternal({ - required Config config, - required github.RepositorySlug slug, - required int number, - required github.MergeMethod mergeMethod, - String? commitMessage, - String? requestSha, -}) async { - // This is retryable so to guard against token expiration we get a fresh - // client each time. - log.info('Attempting to merge ${slug.fullName}/$number.'); - final GithubService gitHubService = await config.createGithubService(slug); - final github.PullRequestMerge pullRequestMerge = await gitHubService.mergePullRequest( - slug, - number, - commitMessage: commitMessage, - mergeMethod: mergeMethod, - requestSha: requestSha, - ); - - if (pullRequestMerge.merged != true) { - throw RetryableException('Pull request ${slug.fullName}/$number could not be merged: ${pullRequestMerge.message}'); - } - - return pullRequestMerge; -} - -final RegExp _kCheckboxPattern = RegExp(r'^\s*-[ ]?\[( |x|X)\]'); -final RegExp _kCommentPattern = RegExp(r''); -final RegExp _kMarkdownLinkRefDef = RegExp(r'^\[[\w\/ -]+\]:'); -final RegExp _kPreLaunchHeader = RegExp(r'## Pre-launch Checklist'); -final RegExp _kDiscordPattern = RegExp(r'#hackers-new'); - -String _sanitizePrBody(String rawPrBody) { - final buffer = StringBuffer(); - bool lastLineWasEmpty = false; - for (final line in rawPrBody.split('\n')) { - if (_kCheckboxPattern.hasMatch(line) || - _kCommentPattern.hasMatch(line) || - _kMarkdownLinkRefDef.hasMatch(line) || - _kPreLaunchHeader.hasMatch(line) || - _kDiscordPattern.hasMatch(line)) { - continue; - } - if (line.trim().isEmpty) { - // we don't need to include multiple empty lines - if (lastLineWasEmpty) { - continue; - } - lastLineWasEmpty = true; - } else { - lastLineWasEmpty = false; - } - buffer.writeln(line); - } - return buffer.toString().trim(); -} diff --git a/auto_submit/lib/validations/approval.dart b/auto_submit/lib/validations/approval.dart deleted file mode 100644 index ee87b0f03..000000000 --- a/auto_submit/lib/validations/approval.dart +++ /dev/null @@ -1,180 +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. - -import 'package:auto_submit/configuration/repository_configuration.dart'; -import 'package:auto_submit/service/config.dart'; -import 'package:auto_submit/model/auto_submit_query_result.dart'; -import 'package:auto_submit/service/github_service.dart'; -import 'package:auto_submit/validations/validation.dart'; -import 'package:github/github.dart' as github; - -import '../service/log.dart'; - -/// Validates that a PR has been approved in accordance with the flutter code -/// review guidelines. -class Approval extends Validation { - Approval({ - required super.config, - }); - - @override - String get name => 'Approval'; - - /// Implements the code review approval logic. - @override - Future validate(QueryResult result, github.PullRequest messagePullRequest) async { - final PullRequest pullRequest = result.repository!.pullRequest!; - final String? author = pullRequest.author!.login; - final List reviews = pullRequest.reviews!.nodes!; - final github.RepositorySlug slug = github.RepositorySlug.full(messagePullRequest.base!.repo!.fullName); - - final RepositoryConfiguration repositoryConfiguration = await config.getRepositoryConfiguration(slug); - - bool approved = false; - String message = ''; - Action action = Action.REMOVE_LABEL; - if (repositoryConfiguration.autoApprovalAccounts.contains(author)) { - log.info('PR ${slug.fullName}/${messagePullRequest.number} approved for roller account: $author'); - return ValidationResult(true, Action.REMOVE_LABEL, ''); - } else { - final GithubService githubService = await config.createGithubService(slug); - final bool authorIsFlutterHacker = - await githubService.isTeamMember(repositoryConfiguration.approvalGroup, author!, slug.owner); - final Approver approver = Approver( - slug, - repositoryConfiguration, - githubService, - author, - reviews, - ); - await approver.computeApproval(); - approved = approver.approved; - - log.info( - 'PR ${slug.fullName}/${messagePullRequest.number} approved $approved, approvers: ${approver.approvers}, remaining approvals: ${approver.remainingReviews}, request authors: ${approver.changeRequestAuthors}', - ); - - String approvedMessage; - final String flutterHackerMessage = (authorIsFlutterHacker) - ? 'You are a member of ${repositoryConfiguration.approvalGroup}' - : 'You are not a member of ${repositoryConfiguration.approvalGroup}'; - // Changes were requested, review count does not matter. - if (approver.changeRequestAuthors.isNotEmpty) { - approved = false; - approvedMessage = - 'This PR has not met approval requirements for merging. Changes were requested by ${approver.changeRequestAuthors}, please make the needed changes and resubmit this PR.\n' - '$flutterHackerMessage and need ${approver.remainingReviews} more review(s) in order to merge this PR.\n'; - } else { - // No changes were requested so check approval count. - approvedMessage = approved - ? 'This PR has met approval requirements for merging.\n' - : 'This PR has not met approval requirements for merging. $flutterHackerMessage and need ${approver.remainingReviews} more review(s) in order to merge this PR.\n'; - if (!approved && authorIsFlutterHacker) { - // Flutter hackers are aware of the review requirements, and can add - // the autosubmit label without waiting on review. - action = Action.IGNORE_TEMPORARILY; - } - } - - message = approved ? approvedMessage : '$approvedMessage\n${Config.pullRequestApprovalRequirementsMessage}'; - } - - return ValidationResult(approved, action, message); - } -} - -class Approver { - Approver( - this.slug, - this.repositoryConfiguration, - this.githubService, - this.author, - this.reviews, - ); - - final github.RepositorySlug slug; - final RepositoryConfiguration repositoryConfiguration; - final GithubService githubService; - final String? author; - final List reviews; - - bool _approved = false; - int _remainingReviews = 2; - final Set _approvers = {}; - final Set _changeRequestAuthors = {}; - final Set _reviewAuthors = {}; - - bool get approved => _approved; - - int get remainingReviews => _remainingReviews; - - Set get approvers => _approvers; - - Set get changeRequestAuthors => _changeRequestAuthors; - - /// Parses the restApi response reviews. - /// - /// If author is a of the defined approval group then it only requires a - /// single review from another approval group member. If the author is not a - /// member of the approval group then it will require two reviews from members - /// of the approval group. - /// - /// Changes requested will supercede any approvals and the autosubmit bot will - /// not make any merges until the change requests are fixed. - Future computeApproval() async { - _remainingReviews = repositoryConfiguration.approvingReviews; - // TODO (ricardoamador) team might be more than one in the future. - final bool authorIsMember = - await githubService.isTeamMember(repositoryConfiguration.approvalGroup, author!, slug.owner); - - // The author counts as 1 review if they are a member of the approval group - // so we need only 1 more review from a member of the approval group. - if (authorIsMember) { - _remainingReviews--; - _approvers.add(author); - } - - final int targetReviewCount = _remainingReviews; - - // Github reviews are returned in chonological order so to avoid the odd - // case where a user requests changes then approves we parse the reviews in - // reverse chronological order. - for (ReviewNode review in reviews.reversed) { - if (review.author!.login == author) { - log.info('Author cannot review own pull request.'); - continue; - } - - // Ignore reviews from non-members/owners. - if (!await githubService.isTeamMember( - repositoryConfiguration.approvalGroup, - review.author!.login!, - slug.owner, - )) { - continue; - } - - final String? state = review.state; - final String? authorLogin = review.author!.login; - // Github keeps all reviews so the same person can provide two reviews and - // possibly bypass the two review rule. Track the reviewers so we can - // account for this. - if (state == APPROVED_STATE && !_reviewAuthors.contains(authorLogin)) { - _approvers.add(authorLogin); - if (_remainingReviews > 0) { - _remainingReviews--; - } - } else if (state == CHANGES_REQUESTED_STATE && !_reviewAuthors.contains(authorLogin)) { - _changeRequestAuthors.add(authorLogin); - if (_remainingReviews < targetReviewCount) { - _remainingReviews++; - } - } - - _reviewAuthors.add(authorLogin); - } - - _approved = (_approvers.length > repositoryConfiguration.approvingReviews - 1) && _changeRequestAuthors.isEmpty; - } -} diff --git a/auto_submit/lib/validations/ci_successful.dart b/auto_submit/lib/validations/ci_successful.dart deleted file mode 100644 index f7a9cfb62..000000000 --- a/auto_submit/lib/validations/ci_successful.dart +++ /dev/null @@ -1,199 +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. - -import 'package:auto_submit/configuration/repository_configuration.dart'; -import 'package:auto_submit/model/auto_submit_query_result.dart'; -import 'package:auto_submit/service/github_service.dart'; -import 'package:auto_submit/validations/validation.dart'; -import 'package:github/github.dart' as github; - -import '../service/config.dart'; -import '../service/log.dart'; - -/// Validates all the CI build/tests ran and were successful. -class CiSuccessful extends Validation { - /// The status checks that are not related to changes in this PR. - static const Set notInAuthorsControl = { - 'tree-status', // flutter/engine repo - 'submit-queue', // packages repo - }; - - CiSuccessful({ - required super.config, - }); - - @override - String get name => 'CiSuccessful'; - - @override - - /// Implements the CI build/tests validations. - Future validate(QueryResult result, github.PullRequest messagePullRequest) async { - bool allSuccess = true; - final github.RepositorySlug slug = messagePullRequest.base!.repo!.slug(); - final int prNumber = messagePullRequest.number!; - final PullRequest pullRequest = result.repository!.pullRequest!; - final Set failures = {}; - - final List statuses = []; - final Commit commit = pullRequest.commits!.nodes!.single.commit!; - final Author author = result.repository!.pullRequest!.author!; - - // Recently most of the repositories have migrated away of using the status - // APIs and for those repos commit.status is null. - if (commit.status != null && commit.status!.contexts!.isNotEmpty) { - statuses.addAll(commit.status!.contexts!); - } - - final RepositoryConfiguration repositoryConfiguration = await config.getRepositoryConfiguration(slug); - final String targetBranch = repositoryConfiguration.defaultBranch; - // Check tree status of repos. If the tree status is not ready, - // we want to hold and wait for the status, same as waiting - // for checks to finish. - final String? baseBranch = messagePullRequest.base!.ref; - if (baseBranch == targetBranch) { - // Only validate tree status where base branch is the default branch. - if (!treeStatusCheck(slug, prNumber, statuses)) { - log.warning('Statuses were not ready for ${slug.fullName}/$prNumber, sha: $commit.'); - return ValidationResult(false, Action.IGNORE_TEMPORARILY, 'Hold to wait for the tree status ready.'); - } - } else { - log.info('Target branch is $baseBranch for ${slug.fullName}/$prNumber, skipping tree status check.'); - } - - // List of labels associated with the pull request. - final List labelNames = (messagePullRequest.labels as List) - .map((github.IssueLabel labelMap) => labelMap.name) - .toList(); - - /// Validate if all statuses have been successful. - allSuccess = validateStatuses(slug, prNumber, author, labelNames, statuses, failures, allSuccess); - - final GithubService gitHubService = await config.createGithubService(slug); - final String? sha = commit.oid; - - final List checkRuns = []; - if (messagePullRequest.head != null && sha != null) { - checkRuns.addAll(await gitHubService.getCheckRuns(slug, sha)); - } - - /// Validate if all checkRuns have succeeded. - allSuccess = validateCheckRuns(slug, prNumber, checkRuns, failures, allSuccess); - - if (!allSuccess && failures.isEmpty) { - return ValidationResult(allSuccess, Action.IGNORE_TEMPORARILY, ''); - } - - final StringBuffer buffer = StringBuffer(); - if (failures.isNotEmpty) { - for (FailureDetail detail in failures) { - buffer.writeln('- The status or check suite ${detail.markdownLink} has failed. Please fix the ' - 'issues identified (or deflake) before re-applying this label.'); - } - } - final Action action = - labelNames.contains(config.overrideTreeStatusLabel) ? Action.IGNORE_FAILURE : Action.REMOVE_LABEL; - return ValidationResult(allSuccess, action, buffer.toString()); - } - - /// Check the tree status. - /// - /// If a repo has a tree status, we should wait for it to show up instead of posting - /// a failure to GitHub pull request. - /// If a repo doesn't have a tree status, simply return `true`. - bool treeStatusCheck(github.RepositorySlug slug, int prNumber, List statuses) { - bool treeStatusValid = false; - if (!Config.reposWithTreeStatus.contains(slug)) { - return true; - } - if (statuses.isEmpty) { - return false; - } - const String treeStatusName = 'tree-status'; - log.info('${slug.fullName}/$prNumber: Validating tree status for ${slug.name}/tree-status, statuses: $statuses'); - - /// Scan list of statuses to see if the tree status exists (this list is expected to be <5 items) - for (ContextNode status in statuses) { - if (status.context == treeStatusName) { - // Does only one tree status need to be set for the condition? - treeStatusValid = true; - } - } - return treeStatusValid; - } - - /// Validate the ci build test run statuses to see which have succeeded and - /// which have failed. - /// - /// Failures will be added the set of overall failures. - /// Returns allSuccess unmodified if there were no failures, false otherwise. - bool validateStatuses( - github.RepositorySlug slug, - int prNumber, - Author author, - List labelNames, - List statuses, - Set failures, - bool allSuccess, - ) { - final String overrideTreeStatusLabel = config.overrideTreeStatusLabel; - log.info('Validating name: ${slug.name}/$prNumber, statuses: $statuses'); - - for (ContextNode status in statuses) { - // How can name be null but presumed to not be null below when added to failure? - final String? name = status.context; - - // If the account author is a roller account do not block merge on flutter-gold check. - if (config.rollerAccounts.contains(author.login!) && slug == Config.engineSlug && name == 'flutter-gold') { - log.info('Skipping status check for flutter-gold for ${slug.fullName}/$prNumber, pr author: $author.'); - continue; - } - - if (status.state != STATUS_SUCCESS) { - if (notInAuthorsControl.contains(name) && labelNames.contains(overrideTreeStatusLabel)) { - continue; - } - allSuccess = false; - if (status.state == STATUS_FAILURE && !notInAuthorsControl.contains(name)) { - failures.add(FailureDetail(name!, status.targetUrl!)); - } - } - } - - return allSuccess; - } - - /// Validate the checkRuns to see if all have completed successfully or not. - /// - /// Failures will be added the set of overall failures. - /// Returns allSuccess unmodified if there were no failures, false otherwise. - bool validateCheckRuns( - github.RepositorySlug slug, - int prNumber, - List checkRuns, - Set failures, - bool allSuccess, - ) { - log.info('Validating name: ${slug.name}/$prNumber, checkRuns: $checkRuns'); - - for (github.CheckRun checkRun in checkRuns) { - final String? name = checkRun.name; - - if (checkRun.conclusion == github.CheckRunConclusion.skipped || - checkRun.conclusion == github.CheckRunConclusion.success || - (checkRun.status == github.CheckRunStatus.completed && - checkRun.conclusion == github.CheckRunConclusion.neutral)) { - // checkrun has passed. - continue; - } else if (checkRun.status == github.CheckRunStatus.completed) { - // checkrun has failed. - log.info('${slug.name}/$prNumber: CheckRun $name failed.'); - failures.add(FailureDetail(name!, checkRun.detailsUrl as String)); - } - allSuccess = false; - } - - return allSuccess; - } -} diff --git a/auto_submit/lib/validations/empty_checks.dart b/auto_submit/lib/validations/empty_checks.dart deleted file mode 100644 index 4aa9e5c6f..000000000 --- a/auto_submit/lib/validations/empty_checks.dart +++ /dev/null @@ -1,34 +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. - -import 'package:auto_submit/model/auto_submit_query_result.dart'; -import 'package:auto_submit/validations/validation.dart'; -import 'package:github/github.dart' as github; - -import '../service/github_service.dart'; - -/// Validates that the list of checks for the PR is not empty. -class EmptyChecks extends Validation { - EmptyChecks({ - required super.config, - }); - - @override - String get name => 'EmptyChecks'; - - @override - - /// Implements the validation to verify the list of checks is not empty. - Future validate(QueryResult result, github.PullRequest messagePullRequest) async { - final github.RepositorySlug slug = messagePullRequest.base!.repo!.slug(); - final GithubService gitHubService = await config.createGithubService(slug); - final PullRequest pullRequest = result.repository!.pullRequest!; - final Commit commit = pullRequest.commits!.nodes!.single.commit!; - final String? sha = commit.oid; - final List checkRuns = await gitHubService.getCheckRuns(slug, sha!); - const String message = '- This commit has no checks. Please check that ci.yaml validation has started' - ' and there are multiple checks. If not, try uploading an empty commit.'; - return ValidationResult(checkRuns.isNotEmpty, Action.REMOVE_LABEL, message); - } -} diff --git a/auto_submit/lib/validations/mergeable.dart b/auto_submit/lib/validations/mergeable.dart deleted file mode 100644 index 8b29c64e7..000000000 --- a/auto_submit/lib/validations/mergeable.dart +++ /dev/null @@ -1,48 +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:auto_submit/model/auto_submit_query_result.dart'; -import 'package:auto_submit/validations/validation.dart'; -import 'package:github/github.dart' as github; -import '../service/log.dart'; - -/// Validates that a pull request is in a mergeable state. -class Mergeable extends Validation { - Mergeable({required super.config}); - - @override - String get name => 'Mergeable'; - - @override - Future validate(QueryResult result, github.PullRequest messagePullRequest) async { - final int pullRequestNumber = messagePullRequest.number!; - final github.RepositorySlug slug = messagePullRequest.base!.repo!.slug(); - final MergeableState mergeableState = result.repository!.pullRequest!.mergeable!; - - log.info('${slug.name}/$pullRequestNumber has mergeable state $mergeableState'); - - switch (mergeableState) { - case MergeableState.MERGEABLE: - return ValidationResult( - true, - Action.REMOVE_LABEL, - 'Pull request ${slug.fullName}/$pullRequestNumber is mergeable', - ); - case MergeableState.UNKNOWN: - return ValidationResult( - false, - Action.IGNORE_TEMPORARILY, - 'Mergeability of pull request ${slug.fullName}/$pullRequestNumber could not be determined at time of merge.', - ); - case MergeableState.CONFLICTING: - // TODO (ricardoamador) monitor to see if we should make this the default class. - // github documentation is poor at best. - return ValidationResult( - false, - Action.REMOVE_LABEL, - 'Pull request ${slug.fullName}/$pullRequestNumber is not in a mergeable state.', - ); - } - } -} diff --git a/auto_submit/lib/validations/required_check_runs.dart b/auto_submit/lib/validations/required_check_runs.dart deleted file mode 100644 index 80cda81fb..000000000 --- a/auto_submit/lib/validations/required_check_runs.dart +++ /dev/null @@ -1,114 +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. - -// Note that we need this file because Github does not expose a field within the -// checks that states whether or not a particular check is required or not. - -import 'package:auto_submit/configuration/repository_configuration.dart'; -import 'package:auto_submit/exception/retryable_exception.dart'; -import 'package:auto_submit/model/auto_submit_query_result.dart' as auto; -import 'package:auto_submit/service/config.dart'; -import 'package:auto_submit/service/github_service.dart'; -import 'package:auto_submit/service/log.dart'; -import 'package:auto_submit/validations/validation.dart'; -import 'package:github/github.dart' as github; -import 'package:retry/retry.dart'; - -const String ciyamlValidation = 'ci.yaml validation'; - -/// Required check runs are check runs noted in the autosubmit.yaml configuration. -/// In order for a pull request to be merged any check runs specified in the -/// required check runs must pass before the bot will merge the pull request -/// regardless of review status. -class RequiredCheckRuns extends Validation { - const RequiredCheckRuns({ - required super.config, - RetryOptions? retryOptions, - }) : retryOptions = retryOptions ?? Config.requiredChecksRetryOptions; - - final RetryOptions retryOptions; - - Future waitForRequiredChecks({ - required github.RepositorySlug slug, - required String sha, - required Set checkNames, - }) async { - final GithubService githubService = await config.createGithubService(slug); - final List targetCheckRuns = []; - - for (String checkRun in checkNames) { - targetCheckRuns.addAll( - await githubService.getCheckRunsFiltered( - slug: slug, - ref: sha, - checkName: checkRun, - ), - ); - } - - bool checksCompleted = true; - - try { - for (github.CheckRun checkRun in targetCheckRuns) { - await retryOptions.retry( - () async { - await _verifyCheckRunCompleted( - slug, - githubService, - checkRun, - ); - }, - retryIf: (Exception e) => e is RetryableException, - ); - } - } catch (e) { - log.warning('Required check has not completed in time. ${e.toString()}'); - checksCompleted = false; - } - - return checksCompleted; - } - - @override - String get name => 'RequiredCheckRuns'; - - @override - Future validate(auto.QueryResult result, github.PullRequest messagePullRequest) async { - final auto.PullRequest pullRequest = result.repository!.pullRequest!; - final auto.Commit commit = pullRequest.commits!.nodes!.single.commit!; - final String? sha = commit.oid; - final github.RepositorySlug slug = messagePullRequest.base!.repo!.slug(); - - final RepositoryConfiguration repositoryConfiguration = await config.getRepositoryConfiguration(slug); - final Set requiredCheckRuns = repositoryConfiguration.requiredCheckRunsOnRevert; - - final bool success = await waitForRequiredChecks(slug: slug, sha: sha!, checkNames: requiredCheckRuns); - - return ValidationResult( - success, - success ? Action.REMOVE_LABEL : Action.IGNORE_TEMPORARILY, - success ? 'All required check runs have completed.' : 'Some of the required checks did not complete in time.', - ); - } -} - -/// Function signature that will be executed with retries. -typedef RetryHandler = Function(); - -/// Simple function to wait on completed checkRuns with retries. -Future _verifyCheckRunCompleted( - github.RepositorySlug slug, - GithubService githubService, - github.CheckRun targetCheckRun, -) async { - final List checkRuns = await githubService.getCheckRunsFiltered( - slug: slug, - ref: targetCheckRun.headSha!, - checkName: targetCheckRun.name, - ); - - if (checkRuns.first.name != targetCheckRun.name || checkRuns.first.conclusion != github.CheckRunConclusion.success) { - throw RetryableException('${targetCheckRun.name} has not yet completed.'); - } -} diff --git a/auto_submit/lib/validations/validation.dart b/auto_submit/lib/validations/validation.dart deleted file mode 100644 index 4fb592362..000000000 --- a/auto_submit/lib/validations/validation.dart +++ /dev/null @@ -1,91 +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. - -import 'package:auto_submit/model/auto_submit_query_result.dart'; - -import '../service/config.dart'; -import 'package:github/github.dart' as github; - -/// GitHub PR state constants. -const APPROVED_STATE = 'APPROVED'; -const CHANGES_REQUESTED_STATE = 'CHANGES_REQUESTED'; - -/// GitHub status state. -const STATUS_SUCCESS = 'SUCCESS'; -const STATUS_FAILURE = 'FAILURE'; - -/// GitHub merge state. -const UNKNOWN_MERGE_STATE = 'UNKNOWN'; - -/// Abstract class defining the signature of the validate method used to -/// implement PR state validations. -abstract class Validation { - const Validation({required this.config}); - - final Config config; - - /// Returns [ValidationResult] after using a [QueryResult] and [PullRequest] to validate - /// a given PR state. - Future validate(QueryResult result, github.PullRequest messagePullRequest); - - String get name; -} - -/// Enum that defines the actions to execute when a validation fails. -enum Action { - /// Add a comment to the PR and remove the autosubmit label. - REMOVE_LABEL, - - /// Ignore the failure and continue merging the PR. - IGNORE_FAILURE, - - /// Do not land the PR but do not remove the autosubmit label either. This is - /// used for temporary states that may fix by themselves. - IGNORE_TEMPORARILY, -} - -/// Holds a result of a validation execution. -/// TODO (ricardoamador) convert this to a record after MergeResult is merged. -class ValidationResult { - ValidationResult(this.result, this.action, this.message); - - /// True if the validation was successful and should not block landing the PR. - bool result; - - /// The action to execute if the validation failed. - Action action; - - /// The message to add to the PR to provide some context to the user about the - /// executed action. - String message; -} - -/// Holds metadata about a given CI build/test failure. -class FailureDetail { - const FailureDetail(this.name, this.url); - - /// The name of the check. - final String name; - - /// The url to the build where the test was executed. - final String url; - - /// A link in markdown format to be added to the GitHub UI. - String get markdownLink => '[$name]($url)'; - - @override - - /// Hash implementation to support adding the results to set data structures. - int get hashCode => 17 * 31 + name.hashCode * 31 + url.hashCode; - - @override - - /// Comparison method to simplify equality validations. - bool operator ==(Object other) { - if (other.runtimeType != runtimeType) { - return false; - } - return other is FailureDetail && other.name == name && other.url == url; - } -} diff --git a/auto_submit/lib/validations/validation_filter.dart b/auto_submit/lib/validations/validation_filter.dart deleted file mode 100644 index f65b60eba..000000000 --- a/auto_submit/lib/validations/validation_filter.dart +++ /dev/null @@ -1,79 +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:auto_submit/configuration/repository_configuration.dart'; -import 'package:auto_submit/service/config.dart'; -import 'package:auto_submit/service/process_method.dart'; -import 'package:auto_submit/validations/approval.dart'; -import 'package:auto_submit/validations/ci_successful.dart'; -import 'package:auto_submit/validations/empty_checks.dart'; -import 'package:auto_submit/validations/mergeable.dart'; -import 'package:auto_submit/validations/required_check_runs.dart'; -import 'package:auto_submit/validations/validation.dart'; - -/// The [ValidationFilter] allows us to pick and choose and the validations to -/// run on a particular type of pull request. -abstract class ValidationFilter { - factory ValidationFilter({ - required Config config, - required ProcessMethod processMethod, - required RepositoryConfiguration repositoryConfiguration, - }) { - switch (processMethod) { - case ProcessMethod.processAutosubmit: - return PullRequestValidationFilter(config, repositoryConfiguration); - case ProcessMethod.processRevert: - return RevertRequestValidationFilter(config, repositoryConfiguration); - default: - throw 'No such processMethod enum value'; - } - } - - Set getValidations(); -} - -/// [PullRequestValidationFilter] returns a Set of validations that we run on -/// all non revert pull requests that will be merged into the mainline branch. -class PullRequestValidationFilter implements ValidationFilter { - PullRequestValidationFilter(this.config, this.repositoryConfiguration); - - final Config config; - final RepositoryConfiguration repositoryConfiguration; - - @override - Set getValidations() { - final Set validationsToRun = {}; - - validationsToRun.add(Approval(config: config)); - // If we are running ci then we need to check the checkRuns and make sure - // there are check runs created. - if (repositoryConfiguration.runCi) { - validationsToRun.add(CiSuccessful(config: config)); - validationsToRun.add(EmptyChecks(config: config)); - } - validationsToRun.add(Mergeable(config: config)); - - return validationsToRun; - } -} - -/// [RevertRequestValidationFilter] returns a Set of validations that we run on -/// all revert pull requests. -class RevertRequestValidationFilter implements ValidationFilter { - RevertRequestValidationFilter(this.config, this.repositoryConfiguration); - - final Config config; - final RepositoryConfiguration repositoryConfiguration; - - @override - Set getValidations() { - final Set validationsToRun = {}; - - validationsToRun.add(Approval(config: config)); - validationsToRun.add(RequiredCheckRuns(config: config)); - validationsToRun.add(Mergeable(config: config)); - - return validationsToRun; - } -} diff --git a/auto_submit/pubspec.yaml b/auto_submit/pubspec.yaml deleted file mode 100644 index 324b13823..000000000 --- a/auto_submit/pubspec.yaml +++ /dev/null @@ -1,41 +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. -name: auto_submit -description: GitHub application for managing pull request submission -version: 0.0.1 -publish_to: none -homepage: https://github.com/flutter/cocoon/blob/main/autosubmit - -environment: - sdk: '>=3.0.0 <4.0.0' - -dependencies: - appengine: 0.13.7 - corsac_jwt: 1.0.0-nullsafety.1 - github: 9.19.0 - googleapis: 11.4.0 - googleapis_auth: 1.4.1 - graphql: 5.2.0-beta.6 - gql: 1.0.1-alpha+1691943394579 - http: 1.1.0 - json_annotation: 4.8.1 - meta: 1.11.0 - neat_cache: 2.0.3 - shelf: 1.4.1 - shelf_router: 1.1.4 - crypto: 3.0.3 - logging: 1.2.0 - retry: 3.1.2 - yaml: 3.1.2 - mutex: 3.1.0 - -dev_dependencies: - build_runner: 2.4.6 - json_serializable: 6.7.1 - flutter_lints: 3.0.1 - mockito: 5.4.2 - test: 1.24.9 - -builders: - json_serializable: 3.3.0 diff --git a/auto_submit/test/configuration/repository_configuration_data.dart b/auto_submit/test/configuration/repository_configuration_data.dart deleted file mode 100644 index dd578b665..000000000 --- a/auto_submit/test/configuration/repository_configuration_data.dart +++ /dev/null @@ -1,48 +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. - -const String sampleConfigNoOverride = ''' - default_branch: main - allow_config_override: false - auto_approval_accounts: - - dependabot[bot] - - dependabot - - DartDevtoolWorkflowBot - approving_reviews: 2 - approval_group: flutter-hackers - run_ci: true - support_no_review_revert: true - required_checkruns_on_revert: - - ci.yaml validation -'''; - -const String sampleConfigRevertReviewRequired = ''' - default_branch: main - allow_config_override: false - auto_approval_accounts: - - dependabot[bot] - - dependabot - - DartDevtoolWorkflowBot - approving_reviews: 2 - approval_group: flutter-hackers - run_ci: true - support_no_review_revert: false - required_checkruns_on_revert: - - ci.yaml validation -'''; - -const String sampleConfigWithOverride = ''' - default_branch: main - allow_config_override: true - auto_approval_accounts: - - dependabot[bot] - - dependabot - - DartDevtoolWorkflowBot - approving_reviews: 2 - approval_group: flutter-hackers - run_ci: true - support_no_review_revert: true - required_checkruns_on_revert: - - ci.yaml validation -'''; diff --git a/auto_submit/test/configuration/repository_configuration_manager_test.dart b/auto_submit/test/configuration/repository_configuration_manager_test.dart deleted file mode 100644 index 37b4ce824..000000000 --- a/auto_submit/test/configuration/repository_configuration_manager_test.dart +++ /dev/null @@ -1,354 +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:auto_submit/configuration/repository_configuration.dart'; -import 'package:auto_submit/configuration/repository_configuration_manager.dart'; -import 'package:github/github.dart'; -import 'package:neat_cache/cache_provider.dart'; -import 'package:neat_cache/neat_cache.dart'; -import 'package:test/test.dart'; - -import '../src/service/fake_config.dart'; -import '../src/service/fake_github_service.dart'; -import 'repository_configuration_data.dart'; - -void main() { - late RepositoryConfigurationManager repositoryConfigurationManager; - late CacheProvider cacheProvider; - late Cache cache; - - late FakeGithubService githubService; - late FakeConfig config; - - setUp(() { - cacheProvider = Cache.inMemoryCacheProvider(5); - cache = Cache(cacheProvider).withPrefix('config'); - githubService = FakeGithubService(); - config = FakeConfig(githubService: githubService); - repositoryConfigurationManager = RepositoryConfigurationManager(config, cache); - }); - - test('Verify cache storage', () async { - const String sampleConfig = ''' - default_branch: main - auto_approval_accounts: - - dependabot[bot] - - dependabot - - DartDevtoolWorkflowBot - approving_reviews: 2 - approval_group: flutter-hackers - run_ci: true - support_no_review_revert: true - required_checkruns_on_revert: - - ci.yaml validation - - Google-testing - - test (ubuntu-latest, 2.18.0) - - cla/google - '''; - - githubService.fileContentsMockList.add(sampleConfig); - final RepositoryConfiguration repositoryConfiguration = - await repositoryConfigurationManager.readRepositoryConfiguration( - RepositorySlug('flutter', 'cocoon'), - ); - - expect(repositoryConfiguration.allowConfigOverride, isFalse); - expect(repositoryConfiguration.defaultBranch, 'main'); - expect(repositoryConfiguration.autoApprovalAccounts.isNotEmpty, isTrue); - expect(repositoryConfiguration.autoApprovalAccounts.length, 3); - expect(repositoryConfiguration.approvingReviews, 2); - expect(repositoryConfiguration.approvalGroup, 'flutter-hackers'); - expect(repositoryConfiguration.runCi, isTrue); - expect(repositoryConfiguration.supportNoReviewReverts, isTrue); - expect(repositoryConfiguration.requiredCheckRunsOnRevert.isNotEmpty, isTrue); - expect(repositoryConfiguration.requiredCheckRunsOnRevert.length, 4); - expect(repositoryConfiguration.requiredCheckRunsOnRevert.contains('ci.yaml validation'), isTrue); - expect(repositoryConfiguration.requiredCheckRunsOnRevert.contains('Google-testing'), isTrue); - expect(repositoryConfiguration.requiredCheckRunsOnRevert.contains('test (ubuntu-latest, 2.18.0)'), isTrue); - expect(repositoryConfiguration.requiredCheckRunsOnRevert.contains('cla/google'), isTrue); - }); - - test('Omitted issues_repository assumes provided slug is for issues', () async { - const String sampleConfig = ''' - default_branch: main - auto_approval_accounts: - - dependabot[bot] - - dependabot - - DartDevtoolWorkflowBot - approving_reviews: 2 - approval_group: flutter-hackers - run_ci: true - support_no_review_revert: true - required_checkruns_on_revert: - - ci.yaml validation - - Google-testing - '''; - - githubService.fileContentsMockList.add(sampleConfig); - final RepositoryConfiguration repositoryConfiguration = - await repositoryConfigurationManager.readRepositoryConfiguration( - RepositorySlug('flutter', 'cocoon'), - ); - - expect(repositoryConfiguration.allowConfigOverride, isFalse); - expect(repositoryConfiguration.defaultBranch, 'main'); - expect(repositoryConfiguration.autoApprovalAccounts.isNotEmpty, isTrue); - expect(repositoryConfiguration.autoApprovalAccounts.length, 3); - expect(repositoryConfiguration.approvingReviews, 2); - expect(repositoryConfiguration.approvalGroup, 'flutter-hackers'); - expect(repositoryConfiguration.runCi, isTrue); - expect(repositoryConfiguration.supportNoReviewReverts, isTrue); - expect(repositoryConfiguration.requiredCheckRunsOnRevert.isNotEmpty, isTrue); - expect(repositoryConfiguration.requiredCheckRunsOnRevert.length, 2); - expect(repositoryConfiguration.requiredCheckRunsOnRevert.contains('ci.yaml validation'), isTrue); - expect(repositoryConfiguration.requiredCheckRunsOnRevert.contains('Google-testing'), isTrue); - }); - - test('Default branch collected if omitted master', () async { - const String sampleConfig = ''' - auto_approval_accounts: - - dependabot[bot] - - dependabot - - DartDevtoolWorkflowBot - approving_reviews: 2 - approval_group: flutter-hackers - run_ci: true - support_no_review_revert: true - required_checkruns_on_revert: - - ci.yaml validation - - Google-testing - '''; - - githubService.fileContentsMockList.add(sampleConfig); - githubService.defaultBranch = 'master'; - final RepositoryConfiguration repositoryConfiguration = - await repositoryConfigurationManager.readRepositoryConfiguration( - RepositorySlug('flutter', 'flutter'), - ); - - expect(repositoryConfiguration.allowConfigOverride, isFalse); - expect(repositoryConfiguration.defaultBranch, 'master'); - expect(repositoryConfiguration.autoApprovalAccounts.isNotEmpty, isTrue); - expect(repositoryConfiguration.autoApprovalAccounts.length, 3); - expect(repositoryConfiguration.approvingReviews, 2); - expect(repositoryConfiguration.approvalGroup, 'flutter-hackers'); - expect(repositoryConfiguration.runCi, isTrue); - expect(repositoryConfiguration.supportNoReviewReverts, isTrue); - expect(repositoryConfiguration.requiredCheckRunsOnRevert.isNotEmpty, isTrue); - expect(repositoryConfiguration.requiredCheckRunsOnRevert.length, 2); - expect(repositoryConfiguration.requiredCheckRunsOnRevert.contains('ci.yaml validation'), isTrue); - expect(repositoryConfiguration.requiredCheckRunsOnRevert.contains('Google-testing'), isTrue); - }); - - test('Default branch collected if omitted main', () async { - const String sampleConfig = ''' - auto_approval_accounts: - - dependabot[bot] - - dependabot - - DartDevtoolWorkflowBot - approving_reviews: 2 - approval_group: flutter-hackers - run_ci: true - support_no_review_revert: true - required_checkruns_on_revert: - - ci.yaml validation - - Google-testing - '''; - - githubService.fileContentsMockList.add(sampleConfig); - githubService.defaultBranch = 'main'; - final RepositoryConfiguration repositoryConfiguration = - await repositoryConfigurationManager.readRepositoryConfiguration( - RepositorySlug('flutter', 'flutter'), - ); - - expect(repositoryConfiguration.allowConfigOverride, isFalse); - expect(repositoryConfiguration.defaultBranch, 'main'); - expect(repositoryConfiguration.autoApprovalAccounts.isNotEmpty, isTrue); - expect(repositoryConfiguration.autoApprovalAccounts.length, 3); - expect(repositoryConfiguration.approvingReviews, 2); - expect(repositoryConfiguration.approvalGroup, 'flutter-hackers'); - expect(repositoryConfiguration.runCi, isTrue); - expect(repositoryConfiguration.supportNoReviewReverts, isTrue); - expect(repositoryConfiguration.requiredCheckRunsOnRevert.isNotEmpty, isTrue); - expect(repositoryConfiguration.requiredCheckRunsOnRevert.length, 2); - expect(repositoryConfiguration.requiredCheckRunsOnRevert.contains('ci.yaml validation'), isTrue); - expect(repositoryConfiguration.requiredCheckRunsOnRevert.contains('Google-testing'), isTrue); - }); - - group('Merging configurations tests', () { - // Sample configuration being used for these tests - /* - default_branch: main - allow_config_override: true - auto_approval_accounts: - - dependabot[bot] - - dependabot - - DartDevtoolWorkflowBot - approving_reviews: 2 - approval_group: flutter-hackers - run_ci: true - support_no_review_revert: true - required_checkruns_on_revert: - - ci.yaml validation - */ - - test('Global config merged with default local config', () { - final RepositoryConfiguration localRepositoryConfiguration = RepositoryConfiguration(); - final RepositoryConfiguration globalRepositoryConfiguration = - RepositoryConfiguration.fromYaml(sampleConfigWithOverride); - final RepositoryConfiguration mergedRepositoryConfiguration = repositoryConfigurationManager.mergeConfigurations( - globalRepositoryConfiguration, - localRepositoryConfiguration, - ); - expect(mergedRepositoryConfiguration.defaultBranch, 'main'); - expect(mergedRepositoryConfiguration.allowConfigOverride, isTrue); - expect(mergedRepositoryConfiguration.autoApprovalAccounts.length, 3); - expect(mergedRepositoryConfiguration.autoApprovalAccounts.contains('dependabot[bot]'), isTrue); - expect(mergedRepositoryConfiguration.autoApprovalAccounts.contains('dependabot'), isTrue); - expect(mergedRepositoryConfiguration.autoApprovalAccounts.contains('DartDevtoolWorkflowBot'), isTrue); - expect(mergedRepositoryConfiguration.approvingReviews, 2); - expect(mergedRepositoryConfiguration.runCi, isTrue); - expect(mergedRepositoryConfiguration.supportNoReviewReverts, isTrue); - expect(mergedRepositoryConfiguration.requiredCheckRunsOnRevert.length, 1); - expect(mergedRepositoryConfiguration.requiredCheckRunsOnRevert.contains('ci.yaml validation'), isTrue); - }); - - test('Auto approval accounts is additive, they cannot be removed', () { - const String expectedAddedApprovalAccount = 'flutter-roller-account'; - const Set localAutoApprovalAccounts = {expectedAddedApprovalAccount}; - final RepositoryConfiguration localRepositoryConfiguration = - RepositoryConfiguration(autoApprovalAccounts: localAutoApprovalAccounts); - final RepositoryConfiguration globalRepositoryConfiguration = - RepositoryConfiguration.fromYaml(sampleConfigWithOverride); - final RepositoryConfiguration mergedRepositoryConfiguration = repositoryConfigurationManager.mergeConfigurations( - globalRepositoryConfiguration, - localRepositoryConfiguration, - ); - expect(mergedRepositoryConfiguration.autoApprovalAccounts.length, 4); - expect(mergedRepositoryConfiguration.autoApprovalAccounts.contains('dependabot[bot]'), isTrue); - expect(mergedRepositoryConfiguration.autoApprovalAccounts.contains('dependabot'), isTrue); - expect(mergedRepositoryConfiguration.autoApprovalAccounts.contains('DartDevtoolWorkflowBot'), isTrue); - expect(mergedRepositoryConfiguration.autoApprovalAccounts.contains(expectedAddedApprovalAccount), isTrue); - }); - - test('Duplicate auto approval account is not added', () { - const String expectedAddedApprovalAccount = 'DartDevtoolWorkflowBot'; - const Set localAutoApprovalAccounts = {expectedAddedApprovalAccount}; - final RepositoryConfiguration localRepositoryConfiguration = - RepositoryConfiguration(autoApprovalAccounts: localAutoApprovalAccounts); - final RepositoryConfiguration globalRepositoryConfiguration = - RepositoryConfiguration.fromYaml(sampleConfigWithOverride); - final RepositoryConfiguration mergedRepositoryConfiguration = repositoryConfigurationManager.mergeConfigurations( - globalRepositoryConfiguration, - localRepositoryConfiguration, - ); - expect(mergedRepositoryConfiguration.autoApprovalAccounts.length, 3); - expect(mergedRepositoryConfiguration.autoApprovalAccounts.contains('dependabot[bot]'), isTrue); - expect(mergedRepositoryConfiguration.autoApprovalAccounts.contains('dependabot'), isTrue); - expect(mergedRepositoryConfiguration.autoApprovalAccounts.contains('DartDevtoolWorkflowBot'), isTrue); - }); - - test('Approving reviews is overridden by local config', () { - const int expectedApprovingReviews = 3; - final RepositoryConfiguration localRepositoryConfiguration = - RepositoryConfiguration(approvingReviews: expectedApprovingReviews); - final RepositoryConfiguration globalRepositoryConfiguration = - RepositoryConfiguration.fromYaml(sampleConfigWithOverride); - final RepositoryConfiguration mergedRepositoryConfiguration = repositoryConfigurationManager.mergeConfigurations( - globalRepositoryConfiguration, - localRepositoryConfiguration, - ); - expect(globalRepositoryConfiguration.approvingReviews != mergedRepositoryConfiguration.approvingReviews, isTrue); - expect(mergedRepositoryConfiguration.approvingReviews, expectedApprovingReviews); - }); - - test('Approving reviews is not overridden if less than global config', () { - const int expectedApprovingReviews = 2; - final RepositoryConfiguration localRepositoryConfiguration = RepositoryConfiguration(approvingReviews: 1); - final RepositoryConfiguration globalRepositoryConfiguration = - RepositoryConfiguration.fromYaml(sampleConfigWithOverride); - final RepositoryConfiguration mergedRepositoryConfiguration = repositoryConfigurationManager.mergeConfigurations( - globalRepositoryConfiguration, - localRepositoryConfiguration, - ); - expect(globalRepositoryConfiguration.approvingReviews == mergedRepositoryConfiguration.approvingReviews, isTrue); - expect(mergedRepositoryConfiguration.approvingReviews, expectedApprovingReviews); - }); - - test('Approval group is overridden if defined', () { - const String expectedApprovalGroup = 'flutter-devs'; - final RepositoryConfiguration localRepositoryConfiguration = - RepositoryConfiguration(approvalGroup: expectedApprovalGroup); - final RepositoryConfiguration globalRepositoryConfiguration = - RepositoryConfiguration.fromYaml(sampleConfigWithOverride); - final RepositoryConfiguration mergedRepositoryConfiguration = repositoryConfigurationManager.mergeConfigurations( - globalRepositoryConfiguration, - localRepositoryConfiguration, - ); - expect(mergedRepositoryConfiguration.approvalGroup, expectedApprovalGroup); - }); - - test('RunCi is updated if it differs from global config', () { - const bool expectedRunCi = false; - final RepositoryConfiguration localRepositoryConfiguration = RepositoryConfiguration(runCi: expectedRunCi); - final RepositoryConfiguration globalRepositoryConfiguration = - RepositoryConfiguration.fromYaml(sampleConfigWithOverride); - final RepositoryConfiguration mergedRepositoryConfiguration = repositoryConfigurationManager.mergeConfigurations( - globalRepositoryConfiguration, - localRepositoryConfiguration, - ); - expect(globalRepositoryConfiguration.runCi != mergedRepositoryConfiguration.runCi, isTrue); - expect(mergedRepositoryConfiguration.runCi, expectedRunCi); - }); - - test('Support no review reverts is updated if it differs from global config', () { - const bool expectedSupportNoReviewReverts = false; - final RepositoryConfiguration localRepositoryConfiguration = - RepositoryConfiguration(supportNoReviewReverts: expectedSupportNoReviewReverts); - final RepositoryConfiguration globalRepositoryConfiguration = - RepositoryConfiguration.fromYaml(sampleConfigWithOverride); - final RepositoryConfiguration mergedRepositoryConfiguration = repositoryConfigurationManager.mergeConfigurations( - globalRepositoryConfiguration, - localRepositoryConfiguration, - ); - expect( - globalRepositoryConfiguration.supportNoReviewReverts != mergedRepositoryConfiguration.supportNoReviewReverts, - isTrue, - ); - expect(mergedRepositoryConfiguration.supportNoReviewReverts, expectedSupportNoReviewReverts); - }); - - test('Required check runs on revert is additive, they cannot be removed', () { - const String expectedRequiredCheckRun = 'Linux Device Doctor Validator'; - const Set localrequiredCheckRunsOnRevert = {expectedRequiredCheckRun}; - final RepositoryConfiguration localRepositoryConfiguration = - RepositoryConfiguration(requiredCheckRunsOnRevert: localrequiredCheckRunsOnRevert); - final RepositoryConfiguration globalRepositoryConfiguration = - RepositoryConfiguration.fromYaml(sampleConfigWithOverride); - final RepositoryConfiguration mergedRepositoryConfiguration = repositoryConfigurationManager.mergeConfigurations( - globalRepositoryConfiguration, - localRepositoryConfiguration, - ); - expect(mergedRepositoryConfiguration.requiredCheckRunsOnRevert.length, 2); - expect(mergedRepositoryConfiguration.requiredCheckRunsOnRevert.contains('ci.yaml validation'), isTrue); - expect(mergedRepositoryConfiguration.requiredCheckRunsOnRevert.contains(expectedRequiredCheckRun), isTrue); - }); - - test('Duplicate required check run on revert is not added', () { - const String expectedRequiredCheckRun = 'ci.yaml validation'; - const Set localRequiredCheckRunsOnRevert = {expectedRequiredCheckRun}; - final RepositoryConfiguration localRepositoryConfiguration = - RepositoryConfiguration(requiredCheckRunsOnRevert: localRequiredCheckRunsOnRevert); - final RepositoryConfiguration globalRepositoryConfiguration = - RepositoryConfiguration.fromYaml(sampleConfigWithOverride); - final RepositoryConfiguration mergedRepositoryConfiguration = repositoryConfigurationManager.mergeConfigurations( - globalRepositoryConfiguration, - localRepositoryConfiguration, - ); - expect(mergedRepositoryConfiguration.requiredCheckRunsOnRevert.length, 1); - expect(mergedRepositoryConfiguration.requiredCheckRunsOnRevert.contains(expectedRequiredCheckRun), isTrue); - }); - }); -} diff --git a/auto_submit/test/configuration/repository_configuration_test.dart b/auto_submit/test/configuration/repository_configuration_test.dart deleted file mode 100644 index 8277d1bef..000000000 --- a/auto_submit/test/configuration/repository_configuration_test.dart +++ /dev/null @@ -1,99 +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:auto_submit/configuration/repository_configuration.dart'; -import 'package:test/test.dart'; - -void main() { - test('Parse config from yaml', () { - const String sampleConfig = ''' - default_branch: main - auto_approval_accounts: - - dependabot[bot] - - dependabot - - DartDevtoolWorkflowBot - approving_reviews: 2 - approval_group: flutter-hackers - run_ci: true - support_no_review_revert: true - required_checkruns_on_revert: - - ci.yaml validation - - Google-testing - '''; - - final RepositoryConfiguration repositoryConfiguration = RepositoryConfiguration.fromYaml(sampleConfig); - expect(repositoryConfiguration.defaultBranch, 'main'); - expect(repositoryConfiguration.autoApprovalAccounts.isNotEmpty, isTrue); - expect(repositoryConfiguration.approvingReviews, 2); - expect(repositoryConfiguration.runCi, isTrue); - expect(repositoryConfiguration.supportNoReviewReverts, isTrue); - expect(repositoryConfiguration.requiredCheckRunsOnRevert.isNotEmpty, isTrue); - }); - - test('Parse config from yaml excluding auto approval accounts', () { - const String sampleConfig = ''' - default_branch: main - approving_reviews: 2 - approval_group: flutter-hackers - run_ci: true - support_no_review_revert: true - required_checkruns_on_revert: - - “ci.yaml validation” - - “Google-testing” - '''; - - final RepositoryConfiguration repositoryConfiguration = RepositoryConfiguration.fromYaml(sampleConfig); - expect(repositoryConfiguration.defaultBranch, 'main'); - expect(repositoryConfiguration.autoApprovalAccounts.isEmpty, isTrue); - expect(repositoryConfiguration.approvingReviews, 2); - expect(repositoryConfiguration.runCi, isTrue); - expect(repositoryConfiguration.supportNoReviewReverts, isTrue); - expect(repositoryConfiguration.requiredCheckRunsOnRevert.isNotEmpty, isTrue); - }); - - test('Parse config from yaml with empty auto_approval_accounts field', () { - const String sampleConfig = ''' - auto_approval_accounts: - approving_reviews: 2 - approval_group: flutter-hackers - run_ci: true - support_no_review_revert: true - required_checkruns_on_revert: - - “ci.yaml validation” - - “Google-testing” - '''; - - final RepositoryConfiguration repositoryConfiguration = RepositoryConfiguration.fromYaml(sampleConfig); - // We will get the default branch later as it does not need to be added to - // the initial configuration. - repositoryConfiguration.defaultBranch = 'main'; - - expect(repositoryConfiguration.allowConfigOverride, false); - expect(repositoryConfiguration.defaultBranch, 'main'); - expect(repositoryConfiguration.autoApprovalAccounts.isEmpty, isTrue); - expect(repositoryConfiguration.approvingReviews, 2); - expect(repositoryConfiguration.runCi, isTrue); - expect(repositoryConfiguration.supportNoReviewReverts, isTrue); - expect(repositoryConfiguration.requiredCheckRunsOnRevert.isNotEmpty, isTrue); - }); - - test('Parse minimal configuration', () { - const String sampleConfig = ''' - approval_group: flutter-hackers - issues_repository: - owner: flutter - repo: flutter - '''; - - final RepositoryConfiguration repositoryConfiguration = RepositoryConfiguration.fromYaml(sampleConfig); - repositoryConfiguration.defaultBranch = 'master'; - - expect(repositoryConfiguration.defaultBranch, 'master'); - expect(repositoryConfiguration.autoApprovalAccounts.isEmpty, isTrue); - expect(repositoryConfiguration.approvingReviews, 2); - expect(repositoryConfiguration.runCi, isTrue); - expect(repositoryConfiguration.supportNoReviewReverts, isTrue); - expect(repositoryConfiguration.requiredCheckRunsOnRevert.isEmpty, isTrue); - }); -} diff --git a/auto_submit/test/git/cli_command_test.dart b/auto_submit/test/git/cli_command_test.dart deleted file mode 100644 index 471c53c84..000000000 --- a/auto_submit/test/git/cli_command_test.dart +++ /dev/null @@ -1,26 +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:io'; - -import 'package:auto_submit/git/cli_command.dart'; -import 'package:test/test.dart'; - -void main() { - group('Testing git command locally', () { - test('Checkout locally.', () async { - String executable = 'ls'; - if (Platform.isWindows) { - executable = 'dir'; - } - - final CliCommand cliCommand = CliCommand(); - final ProcessResult processResult = await cliCommand.runCliCommand( - executable: executable, - arguments: [], - ); - expect(processResult.exitCode, isZero); - }); - }); -} diff --git a/auto_submit/test/git/git_cli_test.dart b/auto_submit/test/git/git_cli_test.dart deleted file mode 100644 index 234799b09..000000000 --- a/auto_submit/test/git/git_cli_test.dart +++ /dev/null @@ -1,79 +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:io'; - -import 'package:auto_submit/git/cli_command.dart'; -import 'package:auto_submit/git/utilities.dart'; -import 'package:auto_submit/git/git_cli.dart'; -import 'package:github/github.dart'; - -import 'package:test/test.dart'; - -void main() { - group('Testing git commands', () { - late CliCommand cliCommand; - late GitCli gitCli; - final String workingDirectory = '${Directory.current.path}/test/repository'; - final String fullRepoCheckoutPath = '$workingDirectory/test_repo'; - final RepositorySlug slug = RepositorySlug('flutter', 'test_repo'); - - late ProcessResult initProcessResult; - - setUp(() async { - cliCommand = CliCommand(); - final Directory directory = Directory(workingDirectory); - directory.createSync(); - gitCli = GitCli(GitAccessMethod.SSH, cliCommand); - initProcessResult = await cliCommand.runCliCommand( - executable: 'git', - arguments: [ - 'init', - slug.name, - '-b', - 'main', - ], - workingDirectory: workingDirectory, - throwOnError: false, - ); - }); - - void validateInit() { - expect(initProcessResult, isNotNull); - expect(initProcessResult.exitCode, isZero); - expect(Directory(fullRepoCheckoutPath).existsSync(), isTrue); - } - - test('isGitRepository()', () async { - validateInit(); - expect(await gitCli.isGitRepository(fullRepoCheckoutPath), isTrue); - }); - - test('createBranch()', () async { - validateInit(); - - final ProcessResult branchProcessResult = await gitCli.createBranch( - newBranchName: 'test_branch', - workingDirectory: fullRepoCheckoutPath, - useCheckout: true, - ); - expect(branchProcessResult, isNotNull); - expect(branchProcessResult.exitCode, isZero); - - final ProcessResult processResult = await cliCommand.runCliCommand( - executable: 'git', - arguments: ['status'], - workingDirectory: fullRepoCheckoutPath, - ); - expect((processResult.stdout as String).contains('On branch test_branch'), isTrue); - }); - - tearDown(() async { - await cliCommand.runCliCommand( - executable: 'rm', - arguments: ['-rf', fullRepoCheckoutPath], - ); - }); - }); -} diff --git a/auto_submit/test/git/git_repository_manager_test.dart b/auto_submit/test/git/git_repository_manager_test.dart deleted file mode 100644 index ca85b2577..000000000 --- a/auto_submit/test/git/git_repository_manager_test.dart +++ /dev/null @@ -1,64 +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:io'; - -import 'package:auto_submit/git/cli_command.dart'; -import 'package:auto_submit/git/utilities.dart'; -import 'package:auto_submit/git/git_cli.dart'; -import 'package:auto_submit/git/git_repository_manager.dart'; -import 'package:github/github.dart'; - -import 'package:test/test.dart'; - -void main() { - group( - 'RepositoryManager', - () { - final String workingDirectoryOutside = Directory.current.parent.parent.path; - - final String workingDirectory = '${Directory.current.path}/test/repository'; - final String targetRepoCheckoutDirectory = '${Directory.current.path}/test/repository/flutter_test'; - final CliCommand cliCommand = CliCommand(); - final GitCli gitCli = GitCli(GitAccessMethod.SSH, cliCommand); - final RepositorySlug slug = RepositorySlug('ricardoamador', 'flutter_test'); - - final GitRepositoryManager gitRepositoryManager = GitRepositoryManager( - slug: slug, - workingDirectory: workingDirectory, - cloneToDirectory: 'flutter_test', - gitCli: gitCli, - ); - - setUp(() { - final Directory directory = Directory(workingDirectory); - directory.createSync(); - }); - - test('cloneRepository()', () async { - await gitRepositoryManager.cloneRepository(); - expect(Directory(targetRepoCheckoutDirectory).existsSync(), isTrue); - }); - - test('cloneRepository() over existing dir.', () async { - await cliCommand.runCliCommand(executable: 'mkdir', arguments: ['$workingDirectory/flutter_test']); - await gitRepositoryManager.cloneRepository(); - expect(Directory('$workingDirectoryOutside/flutter_test').existsSync(), isTrue); - expect(await gitCli.isGitRepository('$workingDirectoryOutside/flutter_test'), isTrue); - }); - - test('deleteRepository()', () async { - await gitRepositoryManager.cloneRepository(); - expect(Directory(targetRepoCheckoutDirectory).existsSync(), isTrue); - await gitRepositoryManager.deleteRepository(); - expect(Directory(targetRepoCheckoutDirectory).existsSync(), isFalse); - }); - - tearDown(() async { - await cliCommand.runCliCommand(executable: 'rm', arguments: ['-rf', targetRepoCheckoutDirectory]); - }); - }, - skip: true, - ); -} diff --git a/auto_submit/test/model/auto_submit_query_result_test.dart b/auto_submit/test/model/auto_submit_query_result_test.dart deleted file mode 100644 index fa440d903..000000000 --- a/auto_submit/test/model/auto_submit_query_result_test.dart +++ /dev/null @@ -1,178 +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. - -import 'dart:convert'; - -import 'package:auto_submit/model/auto_submit_query_result.dart'; -import 'package:test/test.dart'; - -void main() { - late QueryResult queryResult; - late RevertPullRequestData revertPullRequestData; - - group('Auto Submit Models', () { - setUp(() { - queryResult = QueryResult.fromJson(data); - }); - - test('repository values', () async { - final Repository repository = queryResult.repository!; - expect(repository.pullRequest, isNotNull); - }); - - test('pullRequest values', () async { - final PullRequest pullRequest = queryResult.repository!.pullRequest!; - expect(pullRequest.author, isNotNull); - expect(pullRequest.authorAssociation, 'MEMBER'); - expect(pullRequest.commits, isNotNull); - expect(pullRequest.id, 'PR_kwDOA8VHis43rs4_'); - expect(pullRequest.reviews, isNotNull); - expect(pullRequest.title, '[dependabot] Remove human reviewers'); - expect(pullRequest.mergeable, MergeableState.MERGEABLE); - }); - - test('Author values', () async { - final Author author = queryResult.repository!.pullRequest!.author!; - expect(author.login, 'author1'); - }); - - test('Reviews values', () async { - final Reviews reviews = queryResult.repository!.pullRequest!.reviews!; - expect(reviews.nodes, isNotNull); - expect(reviews.nodes!.single, isA()); - final ReviewNode review = reviews.nodes!.single; - expect(review.author, isA()); - expect(review.authorAssociation, 'MEMBER'); - expect(review.state, 'APPROVED'); - }); - - test('Commits values', () async { - final Commits commits = queryResult.repository!.pullRequest!.commits!; - expect(commits.nodes, isNotNull); - final CommitNode commitNode = commits.nodes!.first; - expect(commitNode.commit, isNotNull); - expect(commitNode.commit!.abbreviatedOid, '4009ecc'); - expect(commitNode.commit!.oid, '4009ecc0b6dbf5cb19cb97472147063e7368ec10'); - expect(commitNode.commit!.pushedDate, DateTime.parse('2022-05-11 22:35:03.000Z')); - expect(commitNode.commit!.status, isNull); - }); - }); - - group('Revert pull request models', () { - setUp(() { - revertPullRequestData = RevertPullRequestData.fromJson(revertData); - }); - - test('All fields are present', () { - expect(revertPullRequestData.revertPullRequest, isNotNull); - expect(revertPullRequestData.revertPullRequest!.clientMutationId, isNotNull); - expect(revertPullRequestData.revertPullRequest!.pullRequest, isNotNull); - expect(revertPullRequestData.revertPullRequest!.revertPullRequest, isNotNull); - }); - - test('Client Mutation Id field', () { - expect(revertPullRequestData.revertPullRequest!.clientMutationId, 'ra186026'); - }); - - test('To be reverted PullRequest field.', () { - final PullRequest pullRequest = revertPullRequestData.revertPullRequest!.pullRequest!; - expect(pullRequest.id, 'PR_kwDOIRxr_M5MQ7mV'); - expect(pullRequest.title, 'Adding a TODO comment for testing pull request auto approval.'); - expect(pullRequest.author!.login, 'ricardoamador'); - expect(pullRequest.body, 'This is for testing revert and should be present in the revert mutation.'); - }); - - test('Revert PullRequest field.', () { - final PullRequest revertPullRequest = revertPullRequestData.revertPullRequest!.revertPullRequest!; - expect(revertPullRequest.id, 'PR_kwDOIRxr_M5QN0kD'); - expect(revertPullRequest.title, 'Revert comment in configuration file.'); - expect(revertPullRequest.author!.login, 'ricardoamador'); - expect(revertPullRequest.body, 'Testing revert mutation'); - }); - }); -} - -final Map data = json.decode(dataString) as Map; - -const String dataString = ''' -{ - "repository": { - "pullRequest": { - "author": { - "login": "author1" - }, - "authorAssociation": "MEMBER", - "id": "PR_kwDOA8VHis43rs4_", - "title": "[dependabot] Remove human reviewers", - "mergeable": "MERGEABLE", - "commits": { - "nodes":[ - { - "commit": { - "abbreviatedOid": "4009ecc", - "oid": "4009ecc0b6dbf5cb19cb97472147063e7368ec10", - "committedDate": "2022-05-11T22:35:02Z", - "pushedDate": "2022-05-11T22:35:03Z", - "status":null - } - } - ] - }, - "reviews": { - "nodes": [ - { - "author": { - "login": "keyonghan" - }, - "authorAssociation": "MEMBER", - "state": "APPROVED" - } - ] - } - } - } -} -'''; - -final Map revertData = json.decode(revertRequestString) as Map; - -const String revertRequestString = ''' -{ - "revertPullRequest": { - "clientMutationId": "ra186026", - "pullRequest": { - "author": { - "login": "ricardoamador" - }, - "authorAssociation": "OWNER", - "id": "PR_kwDOIRxr_M5MQ7mV", - "title": "Adding a TODO comment for testing pull request auto approval.", - "number": 18, - "body": "This is for testing revert and should be present in the revert mutation.", - "repository": { - "owner": { - "login": "ricardoamador" - }, - "name": "flutter_test" - } - }, - "revertPullRequest": { - "author": { - "login": "ricardoamador" - }, - "authorAssociation": "OWNER", - "id": "PR_kwDOIRxr_M5QN0kD", - "title": "Revert comment in configuration file.", - "number": 23, - "body": "Testing revert mutation", - "repository": { - "owner": { - "login": "ricardoamador" - }, - "name": "flutter_test" - } - } - } -} -'''; diff --git a/auto_submit/test/model/pull_request_change_type.dart b/auto_submit/test/model/pull_request_change_type.dart deleted file mode 100644 index 2ee29ffac..000000000 --- a/auto_submit/test/model/pull_request_change_type.dart +++ /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. - -import 'package:auto_submit/model/pull_request_data_types.dart'; -import 'package:test/test.dart'; - -void main() { - final List expectedNames = ['change', 'revert']; - - test('Expected string value for enum is returned.', () { - for (PullRequestChangeType prChangeType in PullRequestChangeType.values) { - assert(expectedNames.contains(prChangeType.name)); - } - expect(PullRequestChangeType.values.length, 2); - }); -} diff --git a/auto_submit/test/request_handling/authentication_test.dart b/auto_submit/test/request_handling/authentication_test.dart deleted file mode 100644 index 93cbbd56d..000000000 --- a/auto_submit/test/request_handling/authentication_test.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:auto_submit/request_handling/authentication.dart'; -import 'package:auto_submit/requests/exceptions.dart'; -import 'package:test/test.dart'; -import 'package:shelf/shelf.dart'; - -void main() { - group('CronAuthProvider', () { - late Request request; - late CronAuthProvider auth; - - setUp(() { - auth = const CronAuthProvider(); - }); - - test('throws Unauthenticated with no auth headers', () async { - request = Request('POST', Uri.parse('http://localhost/')); - expect(auth.authenticate(request), throwsA(isA())); - }); - - test('succeeds for App Engine cronjobs', () async { - final Map header = {'X-Appengine-Cron': 'true'}; - request = Request('POST', Uri.parse('http://localhost/'), headers: header); - final bool result = await auth.authenticate(request); - expect(result, true); - }); - }); -} diff --git a/auto_submit/test/requests/check_pull_request_test.dart b/auto_submit/test/requests/check_pull_request_test.dart deleted file mode 100644 index 5fed85a4e..000000000 --- a/auto_submit/test/requests/check_pull_request_test.dart +++ /dev/null @@ -1,810 +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. - -// ignore_for_file: constant_identifier_names -import 'dart:async'; -import 'dart:convert'; -import 'package:auto_submit/configuration/repository_configuration.dart'; -import 'package:auto_submit/service/config.dart'; - -import 'package:auto_submit/requests/check_pull_request.dart'; -import 'package:auto_submit/requests/graphql_queries.dart'; -import 'package:auto_submit/service/log.dart'; -import 'package:github/github.dart'; -import 'package:googleapis/bigquery/v2.dart'; -import 'package:googleapis/pubsub/v1.dart' as pub; -import 'package:graphql/client.dart' hide Request, Response; -import 'package:logging/logging.dart'; -import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; - -import '../configuration/repository_configuration_data.dart'; -import '../service/bigquery_test.dart'; -import '../src/service/fake_bigquery_service.dart'; -import './github_webhook_test_data.dart'; -import '../src/request_handling/fake_pubsub.dart'; -import '../src/request_handling/fake_authentication.dart'; -import '../src/service/fake_config.dart'; -import '../src/service/fake_github_service.dart'; -import '../src/service/fake_graphql_client.dart'; -import '../utilities/mocks.dart'; -import '../utilities/utils.dart' hide createQueryResult; - -const String oid = '6dcb09b5b57875f334f61aebed695e2e4193db5e'; -const String title = 'some_title'; - -void main() { - group('Check CheckPullRequest', () { - late CheckPullRequest checkPullRequest; - late FakeConfig config; - late FakeCronAuthProvider auth; - late FakeGraphQLClient githubGraphQLClient; - late FakeGithubService githubService; - late MockJobsResource jobsResource; - late FakeBigqueryService bigqueryService; - late MockPullRequestsService pullRequests; - final MockGitHub gitHub = MockGitHub(); - late FakePubSub pubsub; - late PullRequestHelper flutterRequest; - late PullRequestHelper cocoonRequest; - late List expectedOptions; - late QueryOptions flutterOption; - late QueryOptions cocoonOption; - const String testSubscription = 'test-sub'; - const String testTopic = 'test-topic'; - const String rollorAuthor = 'engine-flutter-autoroll'; - const String labelName = 'warning: land on red to fix tree breakage'; - const String cocoonRepo = 'cocoon'; - const String noAutosubmitLabel = 'no_autosubmit'; - - setUp(() { - githubGraphQLClient = FakeGraphQLClient(); - auth = FakeCronAuthProvider(); - pubsub = FakePubSub(); - expectedOptions = []; - githubService = FakeGithubService(); - - githubGraphQLClient.mutateResultForOptions = (MutationOptions options) => createFakeQueryResult(); - - githubGraphQLClient.queryResultForOptions = (QueryOptions options) { - expect(options.variables['sOwner'], 'flutter'); - final String? repoName = options.variables['sName'] as String?; - if (repoName == 'flutter') { - return createQueryResult(flutterRequest); - } else if (repoName == 'cocoon') { - return createQueryResult(cocoonRequest); - } else { - fail('unexpected repo $repoName'); - } - }; - - final FindPullRequestsWithReviewsQuery findPullRequestsWithReviewsQueryFlutter = FindPullRequestsWithReviewsQuery( - repositoryOwner: 'flutter', - repositoryName: 'flutter', - pullRequestNumber: 0, - ); - - flutterOption = QueryOptions( - document: findPullRequestsWithReviewsQueryFlutter.documentNode, - fetchPolicy: FetchPolicy.noCache, - variables: findPullRequestsWithReviewsQueryFlutter.variables, - ); - - final FindPullRequestsWithReviewsQuery findPullRequestsWithReviewsQueryCocoon = FindPullRequestsWithReviewsQuery( - repositoryOwner: 'flutter', - repositoryName: 'cocoon', - pullRequestNumber: 1, - ); - - cocoonOption = QueryOptions( - document: findPullRequestsWithReviewsQueryCocoon.documentNode, - fetchPolicy: FetchPolicy.noCache, - variables: findPullRequestsWithReviewsQueryCocoon.variables, - ); - - githubService.checkRunsData = checkRunsMock; - githubService.compareTwoCommitsData = compareTwoCommitsMock; - githubService.successMergeData = successMergeMock; - githubService.createCommentData = createCommentMock; - githubService.commitData = commitMock; - jobsResource = MockJobsResource(); - bigqueryService = FakeBigqueryService(jobsResource); - config = FakeConfig( - githubService: githubService, - githubGraphQLClient: githubGraphQLClient, - githubClient: gitHub, - ); - config.bigqueryService = bigqueryService; - config.repositoryConfigurationMock = RepositoryConfiguration.fromYaml(sampleConfigNoOverride); - pullRequests = MockPullRequestsService(); - when(gitHub.pullRequests).thenReturn(pullRequests); - when(pullRequests.get(any, any)).thenAnswer( - (_) async => PullRequest( - number: 123, - state: 'open', - ), - ); - - when(jobsResource.query(captureAny, any)).thenAnswer((Invocation invocation) { - return Future.value( - QueryResponse.fromJson(jsonDecode(insertDeleteUpdateSuccessResponse) as Map), - ); - }); - }); - - void verifyQueries(List expectedOptions) { - githubGraphQLClient.verifyQueries(expectedOptions); - } - - test('Multiple identical messages are processed once', () async { - final PullRequest pullRequest1 = generatePullRequest( - prNumber: 0, - repoName: cocoonRepo, - ); - // 'member' is in the review nodes and 'author1' is the pr author. - githubService.isTeamMemberMockMap['member'] = true; - githubService.isTeamMemberMockMap['author1'] = true; - githubService.pullRequestData = pullRequest1; - for (int i = 0; i < 2; i++) { - unawaited(pubsub.publish('auto-submit-queue-sub', pullRequest1)); - } - - checkPullRequest = CheckPullRequest( - config: config, - pubsub: pubsub, - cronAuthProvider: auth, - ); - cocoonRequest = PullRequestHelper( - prNumber: 0, - lastCommitHash: oid, - ); - - final Map expectedMergeRequestMap = {}; - expectedMergeRequestMap[0] = RepositorySlug('flutter', cocoonRepo); - - await checkPullRequest.get(); - - githubService.verifyMergePullRequests(expectedMergeRequestMap); - - expect(0, pubsub.messagesQueue.length); - }); - - test('Closed PRs are not processed', () async { - final PullRequest pullRequest1 = generatePullRequest( - prNumber: 0, - repoName: cocoonRepo, - state: 'close', - ); - githubService.pullRequestData = pullRequest1; - when(pullRequests.get(any, any)).thenAnswer( - (_) async => PullRequest( - number: 0, - state: 'close', - ), - ); - for (int i = 0; i < 2; i++) { - unawaited(pubsub.publish('auto-submit-queue-sub', pullRequest1)); - } - - checkPullRequest = CheckPullRequest( - config: config, - pubsub: pubsub, - cronAuthProvider: auth, - ); - cocoonRequest = PullRequestHelper( - prNumber: 0, - lastCommitHash: oid, - ); - await checkPullRequest.get(); - - githubGraphQLClient.verifyMutations( - [], - ); - expect(0, pubsub.messagesQueue.length); - }); - - test('Merge exception is handled correctly', () async { - final PullRequest pullRequest1 = generatePullRequest(prNumber: 0); - final PullRequest pullRequest2 = generatePullRequest( - prNumber: 1, - repoName: cocoonRepo, - ); - - githubService.pullRequestData = pullRequest1; - // 'member' is in the review nodes and 'author1' is the pr author. - githubService.isTeamMemberMockMap['member'] = true; - githubService.isTeamMemberMockMap['author1'] = true; - - final List pullRequests = [pullRequest1, pullRequest2]; - for (PullRequest pr in pullRequests) { - unawaited(pubsub.publish(testTopic, pr)); - } - - checkPullRequest = CheckPullRequest( - config: config, - pubsub: pubsub, - cronAuthProvider: auth, - ); - flutterRequest = PullRequestHelper( - prNumber: 0, - lastCommitHash: oid, - ); - cocoonRequest = PullRequestHelper( - prNumber: 1, - lastCommitHash: oid, - ); - - githubService.useMergeRequestMockList = true; - githubService.pullRequestMergeMockList.add( - PullRequestMerge( - merged: false, - message: 'Unable to merge pull request', - ), - ); - githubService.pullRequestMergeMockList.add( - PullRequestMerge( - merged: true, - sha: 'sha', - message: 'Pull request merged successfully', - ), - ); - - final Map expectedMergeRequestMap = {}; - expectedMergeRequestMap[0] = RepositorySlug( - 'flutter', - 'flutter', - ); - expectedMergeRequestMap[1] = RepositorySlug( - 'flutter', - cocoonRepo, - ); - - final List records = []; - log.onRecord.listen((LogRecord record) => records.add(record)); - // this is the test. - await checkPullRequest.get(); - // every failure is now acknowledged from the queue. - expect(pubsub.messagesQueue.length, 0); - final List errorLogs = records.where((LogRecord record) => record.level == Level.SEVERE).toList(); - expect(errorLogs.length, 1); - expect(errorLogs[0].message.contains('Failed to merge'), true); - pubsub.messagesQueue.clear(); - }); - - test('Merges PR with successful status and checks', () async { - final PullRequest pullRequest1 = generatePullRequest(prNumber: 0); - final PullRequest pullRequest2 = generatePullRequest( - prNumber: 1, - repoName: cocoonRepo, - ); - githubService.pullRequestData = pullRequest1; - // 'octocat' is the pr author from generatePullRequest calls. - // 'member' is in the review nodes and 'author1' is the pr author. - githubService.isTeamMemberMockMap['member'] = true; - githubService.isTeamMemberMockMap['author1'] = true; - final List pullRequests = [pullRequest1, pullRequest2]; - for (PullRequest pr in pullRequests) { - unawaited(pubsub.publish(testTopic, pr)); - } - - checkPullRequest = CheckPullRequest( - config: config, - pubsub: pubsub, - cronAuthProvider: auth, - ); - - flutterRequest = PullRequestHelper( - prNumber: 0, - lastCommitHash: oid, - ); - cocoonRequest = PullRequestHelper( - prNumber: 1, - lastCommitHash: oid, - ); - - await checkPullRequest.get(); - expectedOptions.add(flutterOption); - expectedOptions.add(cocoonOption); - verifyQueries(expectedOptions); - - githubService.useMergeRequestMockList = true; - githubService.pullRequestMergeMockList.add( - PullRequestMerge( - merged: true, - sha: 'sha1', - message: 'Pull request merged successfully', - ), - ); - githubService.pullRequestMergeMockList.add( - PullRequestMerge( - merged: true, - sha: 'sha2', - message: 'Pull request merged successfully', - ), - ); - - final Map expectedMergeRequestMap = {}; - expectedMergeRequestMap[0] = RepositorySlug('flutter', 'flutter'); - expectedMergeRequestMap[1] = RepositorySlug('flutter', cocoonRepo); - - githubService.verifyMergePullRequests(expectedMergeRequestMap); - - assert(pubsub.messagesQueue.isEmpty); - }); - - test('Merges unapproved PR from autoroller', () async { - final PullRequest pullRequest = generatePullRequest( - prNumber: 0, - author: rollorAuthor, - ); - githubService.pullRequestData = pullRequest; - unawaited(pubsub.publish(testTopic, pullRequest)); - - checkPullRequest = CheckPullRequest( - config: config, - pubsub: pubsub, - cronAuthProvider: auth, - approverProvider: (Config config) => MockApproverService(), - ); - - flutterRequest = PullRequestHelper( - prNumber: 0, - author: 'dependabot', - reviews: const [], - lastCommitHash: oid, - ); - - await checkPullRequest.get(); - expectedOptions.add(flutterOption); - verifyQueries(expectedOptions); - - final Map expectedMergeRequestMap = {}; - expectedMergeRequestMap[0] = RepositorySlug( - 'flutter', - 'flutter', - ); - - githubService.mergeRequestMock = PullRequestMerge( - merged: true, - sha: 'sha1', - message: 'Pull request merged successfully', - ); - - githubService.verifyMergePullRequests(expectedMergeRequestMap); - - assert(pubsub.messagesQueue.isEmpty); - }); - - test('Merges PR with failed tree status if override tree status label is provided', () async { - final PullRequest pullRequest = generatePullRequest( - prNumber: 0, - labelName: labelName, - ); - // 'member' is in the review nodes and 'author1' is the pr author. - githubService.isTeamMemberMockMap['member'] = true; - githubService.isTeamMemberMockMap['author1'] = true; - githubService.pullRequestData = pullRequest; - unawaited( - pubsub.publish( - testTopic, - pullRequest, - ), - ); - - checkPullRequest = CheckPullRequest( - config: config, - pubsub: pubsub, - cronAuthProvider: auth, - ); - - flutterRequest = PullRequestHelper( - prNumber: 0, - lastCommitHash: oid, - lastCommitStatuses: const [ - StatusHelper.flutterBuildFailure, - ], - ); - - await checkPullRequest.get(); - expectedOptions.add(flutterOption); - verifyQueries(expectedOptions); - - final Map expectedMergeRequestMap = {}; - expectedMergeRequestMap[0] = RepositorySlug( - 'flutter', - 'flutter', - ); - - githubService.mergeRequestMock = PullRequestMerge( - merged: true, - sha: 'sha1', - message: 'Pull request merged successfully', - ); - - githubService.verifyMergePullRequests(expectedMergeRequestMap); - - assert(pubsub.messagesQueue.isEmpty); - }); - - test('Merges a clean revert PR with in progress tests', () async { - final PullRequest pullRequest = generatePullRequest(prNumber: 0); - githubService.pullRequestData = pullRequest; - // 'member' is in the review nodes and 'author1' is the pr author. - githubService.isTeamMemberMockMap['member'] = true; - githubService.isTeamMemberMockMap['author1'] = true; - unawaited(pubsub.publish(testTopic, pullRequest)); - checkPullRequest = CheckPullRequest( - config: config, - pubsub: pubsub, - cronAuthProvider: auth, - ); - - flutterRequest = PullRequestHelper( - prNumber: 0, - lastCommitHash: oid, - lastCommitStatuses: const [ - StatusHelper.flutterBuildSuccess, - ], - lastCommitMessage: 'Revert "This is a test PR" This reverts commit abc.', - ); - - await checkPullRequest.get(); - expectedOptions.add(flutterOption); - verifyQueries(expectedOptions); - - final Map expectedMergeRequestMap = {}; - expectedMergeRequestMap[0] = RepositorySlug( - 'flutter', - 'flutter', - ); - - githubService.mergeRequestMock = PullRequestMerge( - merged: true, - sha: 'sha1', - message: 'Pull request merged successfully', - ); - - githubService.verifyMergePullRequests(expectedMergeRequestMap); - - assert(pubsub.messagesQueue.isEmpty); - }); - - test('Merges PR with successful checks on repo without tree status', () async { - final PullRequest pullRequest = generatePullRequest( - prNumber: 1, - repoName: cocoonRepo, - ); - githubService.pullRequestData = pullRequest; - // 'member' is in the review nodes and 'author1' is the pr author. - githubService.isTeamMemberMockMap['member'] = true; - githubService.isTeamMemberMockMap['author1'] = true; - unawaited(pubsub.publish(testTopic, pullRequest)); - - checkPullRequest = CheckPullRequest( - config: config, - pubsub: pubsub, - cronAuthProvider: auth, - ); - - cocoonRequest = PullRequestHelper( - lastCommitHash: oid, - lastCommitStatuses: const [], - ); - - await checkPullRequest.get(); - expectedOptions.add(cocoonOption); - verifyQueries(expectedOptions); - - final Map expectedMergeRequestMap = {}; - expectedMergeRequestMap[1] = RepositorySlug( - 'flutter', - cocoonRepo, - ); - - githubService.mergeRequestMock = PullRequestMerge( - merged: true, - sha: 'sha1', - message: 'Pull request merged successfully', - ); - - githubService.verifyMergePullRequests(expectedMergeRequestMap); - - assert(pubsub.messagesQueue.isEmpty); - }); - - test('Merges PR with neutral status checkrun', () async { - final PullRequest pullRequest1 = generatePullRequest(prNumber: 0); - final PullRequest pullRequest2 = generatePullRequest( - prNumber: 1, - repoName: cocoonRepo, - ); - githubService.pullRequestData = pullRequest1; - // 'member' is in the review nodes and 'author1' is the pr author. - githubService.isTeamMemberMockMap['member'] = true; - githubService.isTeamMemberMockMap['author1'] = true; - final List pullRequests = [pullRequest1, pullRequest2]; - for (PullRequest pr in pullRequests) { - unawaited(pubsub.publish(testTopic, pr)); - } - githubService.checkRunsData = neutralCheckRunsMock; - checkPullRequest = CheckPullRequest( - config: config, - pubsub: pubsub, - cronAuthProvider: auth, - ); - flutterRequest = PullRequestHelper( - prNumber: 0, - lastCommitHash: oid, - ); - cocoonRequest = PullRequestHelper( - prNumber: 1, - lastCommitHash: oid, - ); - - await checkPullRequest.get(); - expectedOptions.add(flutterOption); - expectedOptions.add(cocoonOption); - verifyQueries(expectedOptions); - assert(pubsub.messagesQueue.isEmpty); - }); - - test('Removes the label for the PR with failed tests', () async { - final PullRequest pullRequest1 = generatePullRequest(prNumber: 0); - final PullRequest pullRequest2 = generatePullRequest( - prNumber: 1, - repoName: cocoonRepo, - ); - githubService.pullRequestData = pullRequest1; - // 'member' is in the review nodes and 'author1' is the pr author. - githubService.isTeamMemberMockMap['member'] = true; - githubService.isTeamMemberMockMap['author1'] = true; - final List pullRequests = [pullRequest1, pullRequest2]; - for (PullRequest pr in pullRequests) { - unawaited(pubsub.publish(testTopic, pr)); - } - githubService.checkRunsData = failedCheckRunsMock; - checkPullRequest = CheckPullRequest( - config: config, - pubsub: pubsub, - cronAuthProvider: auth, - ); - flutterRequest = PullRequestHelper( - prNumber: 0, - lastCommitHash: oid, - ); - cocoonRequest = PullRequestHelper( - prNumber: 1, - lastCommitHash: oid, - ); - - await checkPullRequest.get(); - expectedOptions.add(flutterOption); - expectedOptions.add(cocoonOption); - verifyQueries(expectedOptions); - assert(pubsub.messagesQueue.isEmpty); - }); - - test('Removes the label for the PR with failed status', () async { - final PullRequest pullRequest = generatePullRequest(prNumber: 0); - githubService.pullRequestData = pullRequest; - // 'member' is in the review nodes and 'author1' is the pr author. - githubService.isTeamMemberMockMap['member'] = true; - githubService.isTeamMemberMockMap['author1'] = true; - unawaited(pubsub.publish(testTopic, pullRequest)); - - checkPullRequest = CheckPullRequest( - config: config, - pubsub: pubsub, - cronAuthProvider: auth, - ); - - flutterRequest = PullRequestHelper( - lastCommitHash: oid, - lastCommitStatuses: const [ - StatusHelper.flutterBuildSuccess, - StatusHelper.otherStatusFailure, - ], - ); - - await checkPullRequest.get(); - expectedOptions.add(flutterOption); - verifyQueries(expectedOptions); - assert(pubsub.messagesQueue.isEmpty); - }); - - test('Removes the label if non member does not have at least 2 member reviews', () async { - final PullRequest pullRequest = generatePullRequest( - prNumber: 0, - ); - githubService.pullRequestData = pullRequest; - // 'octocat' is the pr author from generatePullRequest calls. - githubService.isTeamMemberMockMap['octocat'] = false; - unawaited(pubsub.publish(testTopic, pullRequest)); - - checkPullRequest = CheckPullRequest( - config: config, - pubsub: pubsub, - cronAuthProvider: auth, - ); - - flutterRequest = PullRequestHelper( - authorAssociation: '', - lastCommitHash: oid, - lastCommitStatuses: const [ - StatusHelper.flutterBuildSuccess, - ], - ); - - await checkPullRequest.get(); - expectedOptions.add(flutterOption); - verifyQueries(expectedOptions); - assert(pubsub.messagesQueue.isEmpty); - }); - - test('Removes the label for the PR with null checks and statuses', () async { - final PullRequest pullRequest = generatePullRequest(prNumber: 0); - githubService.pullRequestData = pullRequest; - // 'octocat' is the pr author from generatePullRequest calls. - githubService.isTeamMemberMockMap['octocat'] = true; - unawaited(pubsub.publish(testTopic, pullRequest)); - - githubService.checkRunsData = emptyCheckRunsMock; - checkPullRequest = CheckPullRequest( - config: config, - pubsub: pubsub, - cronAuthProvider: auth, - ); - - flutterRequest = PullRequestHelper( - lastCommitHash: oid, - lastCommitStatuses: const [], - ); - - await checkPullRequest.get(); - expectedOptions.add(flutterOption); - verifyQueries(expectedOptions); - assert(pubsub.messagesQueue.isEmpty); - }); - - test('Does not merge PR with in progress checks', () async { - final PullRequest pullRequest1 = generatePullRequest(prNumber: 0); - final PullRequest pullRequest2 = generatePullRequest( - prNumber: 1, - repoName: cocoonRepo, - ); - githubService.pullRequestData = pullRequest1; - // 'member' is in the review nodes and 'author1' is the pr author. - githubService.isTeamMemberMockMap['member'] = true; - githubService.isTeamMemberMockMap['author1'] = true; - final List pullRequests = [pullRequest1, pullRequest2]; - for (PullRequest pr in pullRequests) { - unawaited(pubsub.publish(testTopic, pr)); - } - githubService.checkRunsData = inProgressCheckRunsMock; - checkPullRequest = CheckPullRequest( - config: config, - pubsub: pubsub, - cronAuthProvider: auth, - ); - flutterRequest = PullRequestHelper(prNumber: 0); - cocoonRequest = PullRequestHelper(prNumber: 1); - await checkPullRequest.get(); - expectedOptions.add(flutterOption); - expectedOptions.add(cocoonOption); - verifyQueries(expectedOptions); - expect(pubsub.messagesQueue.length, 2); - pubsub.messagesQueue.clear(); - }); - - test('Does not merge PR if no autosubmit label any more', () async { - final PullRequest pullRequest1 = generatePullRequest( - prNumber: 0, - autosubmitLabel: noAutosubmitLabel, - ); - final PullRequest pullRequest2 = generatePullRequest( - prNumber: 1, - autosubmitLabel: noAutosubmitLabel, - repoName: cocoonRepo, - ); - githubService.pullRequestData = pullRequest1; - final List pullRequests = [pullRequest1, pullRequest2]; - for (PullRequest pr in pullRequests) { - unawaited(pubsub.publish(testTopic, pr)); - } - checkPullRequest = CheckPullRequest( - config: config, - pubsub: pubsub, - cronAuthProvider: auth, - ); - flutterRequest = PullRequestHelper(prNumber: 0); - cocoonRequest = PullRequestHelper(prNumber: 1); - await checkPullRequest.get(); - assert(pubsub.messagesQueue.isEmpty); - }); - - test('Self review is disallowed', () async { - final PullRequest pullRequest = generatePullRequest( - prNumber: 0, - author: 'some_rando', - ); - githubService.pullRequestData = pullRequest; - // 'octocat' is the pr author from generatePullRequest calls. - githubService.isTeamMemberMockMap['some_rando'] = true; - unawaited(pubsub.publish(testTopic, pullRequest)); - checkPullRequest = CheckPullRequest( - config: config, - pubsub: pubsub, - cronAuthProvider: auth, - ); - flutterRequest = PullRequestHelper( - author: 'some_rando', - lastCommitHash: oid, - authorAssociation: 'MEMBER', - reviews: [ - const PullRequestReviewHelper( - authorName: 'some_rando', - state: ReviewState.APPROVED, - memberType: MemberType.MEMBER, - ), - ], - lastCommitStatuses: const [ - StatusHelper.flutterBuildSuccess, - ], - ); - await checkPullRequest.get(); - expectedOptions.add(flutterOption); - verifyQueries(expectedOptions); - // Re-add to queue to poll for reviews - assert(pubsub.messagesQueue.isNotEmpty); - }); - - test('All messages are pulled', () async { - for (int i = 0; i < 3; i++) { - final PullRequest pullRequest = generatePullRequest( - prNumber: i, - repoName: cocoonRepo, - ); - unawaited( - pubsub.publish( - 'auto-submit-queue-sub', - pullRequest, - ), - ); - } - - checkPullRequest = CheckPullRequest( - config: config, - pubsub: pubsub, - cronAuthProvider: auth, - ); - cocoonRequest = PullRequestHelper( - prNumber: 0, - lastCommitHash: oid, - ); - final List messages = await checkPullRequest.pullMessages(testSubscription, 5, 5); - expect(messages.length, 3); - }); - }); -} - -QueryResult createQueryResult(PullRequestHelper pullRequest) { - return createFakeQueryResult( - data: { - 'repository': { - 'pullRequest': pullRequest.toEntry().cast(), - }, - }, - ); -} - -Map getMergePullRequestVariables( - String id, - String number, -) { - return { - 'id': id, - 'oid': oid, - 'title': '$title (#$number)', - }; -} diff --git a/auto_submit/test/requests/github_webhook_test.dart b/auto_submit/test/requests/github_webhook_test.dart deleted file mode 100644 index 96b262b05..000000000 --- a/auto_submit/test/requests/github_webhook_test.dart +++ /dev/null @@ -1,63 +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. - -import 'dart:convert'; -import 'dart:typed_data'; - -import 'package:auto_submit/requests/github_webhook.dart'; -import 'package:auto_submit/requests/exceptions.dart'; -import 'package:crypto/crypto.dart'; -import 'package:github/github.dart'; -import 'package:shelf/shelf.dart'; -import 'package:test/test.dart'; - -import './github_webhook_test_data.dart'; -import '../src/request_handling/fake_pubsub.dart'; -import '../src/service/fake_config.dart'; - -void main() { - group('Check Webhook', () { - late Request req; - late GithubWebhook githubWebhook; - const String keyString = 'not_a_real_key'; - final FakeConfig config = FakeConfig(webhookKey: keyString); - final FakePubSub pubsub = FakePubSub(); - late Map validHeader; - late Map inValidHeader; - - String getHmac(Uint8List list, Uint8List key) { - final Hmac hmac = Hmac(sha1, key); - return hmac.convert(list).toString(); - } - - setUp(() { - githubWebhook = GithubWebhook(config: config, pubsub: pubsub); - }); - - test('call handler to handle the post request', () async { - final Uint8List body = utf8.encode(generateWebhookEvent()); - final Uint8List key = utf8.encode(keyString); - final String hmac = getHmac(body, key); - validHeader = {'X-Hub-Signature': 'sha1=$hmac', 'X-GitHub-Event': 'yes'}; - req = Request('POST', Uri.parse('http://localhost/'), body: generateWebhookEvent(), headers: validHeader); - final Response response = await githubWebhook.post(req); - final String resBody = await response.readAsString(); - final reqBody = json.decode(resBody) as Map; - final List labels = PullRequest.fromJson(reqBody['pull_request'] as Map).labels!; - expect(labels[0].name, 'cla: yes'); - expect(labels[1].name, 'autosubmit'); - }); - - test('Rejects invalid hmac', () async { - inValidHeader = {'X-GitHub-Event': 'pull_request', 'X-Hub-Signature': 'bar'}; - req = Request('POST', Uri.parse('http://localhost/'), body: 'Hello, World!', headers: inValidHeader); - await expectLater(githubWebhook.post(req), throwsA(isA())); - }); - - test('Rejects missing headers', () async { - req = Request('POST', Uri.parse('http://localhost/'), body: generateWebhookEvent()); - await expectLater(githubWebhook.post(req), throwsA(isA())); - }); - }); -} diff --git a/auto_submit/test/requests/github_webhook_test_data.dart b/auto_submit/test/requests/github_webhook_test_data.dart deleted file mode 100644 index ace27b74a..000000000 --- a/auto_submit/test/requests/github_webhook_test_data.dart +++ /dev/null @@ -1,562 +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. - -import 'dart:convert'; - -import 'package:auto_submit/service/config.dart'; -import 'package:github/github.dart'; - -String generateWebhookEvent({ - String? labelName, - String? autosubmitLabel, - String? repoName, - String? login, - String? authorAssociation, -}) { - return '''{ - "action": "open", - "number": 1598, - "sender": { - "login": "matanlurey", - "id": 168174, - "node_id": "MDQ6VXNlcjE2ODE3NA==" - }, - "pull_request": { - "id": 1, - "number": 1347, - "state": "open", - "title": "Amazing new feature", - "user": { - "login": "${login ?? "octocat"}", - "id": 1 - }, - "body": "Please pull these awesome changes in!", - "labels": [ - { - "id": 487496476, - "name": "${labelName ?? "cla: yes"}" - }, - { - "id": 284437560, - "name": "${autosubmitLabel ?? "autosubmit"}" - } - ], - "created_at": "2011-01-26T19:01:12Z", - "head": { - "label": "octocat:new-topic", - "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e", - "repo": { - "id": 1296269, - "name": "Hello-World", - "full_name": "octocat/Hello-World", - "owner": { - "login": "octocat", - "id": 1, - "avatar_url": "https://github.com/images/error/octocat_happy.gif", - "html_url": "https://github.com/octocat" - } - } - }, - "base": { - "label": "octocat:master", - "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e", - "repo": { - "id": 1296269, - "name": "${repoName ?? "flutter"}", - "full_name": "${login ?? "flutter"}/${repoName ?? "flutter"}", - "owner": { - "login": "${login ?? "flutter"}", - "id": 1, - "avatar_url": "https://github.com/images/error/octocat_happy.gif", - "html_url": "https://github.com/octocat" - } - } - }, - "author_association": "${authorAssociation ?? "OWNER"}", - "mergeable": true, - "mergeable_state": "clean" - } - }'''; -} - -PullRequest generatePullRequest({ - String labelName = 'cla: yes', - String? autosubmitLabel = Config.kAutosubmitLabel, - String repoName = 'flutter', - String login = 'flutter', - String authorAssociation = 'OWNER', - String author = 'octocat', - int prNumber = 1347, - String state = 'open', - String? body = 'Please pull these awesome changes in!', - String title = 'Amazing new feature', - bool? mergeable = true, - String baseRef = 'main', - DateTime? mergedAt, -}) { - return PullRequest.fromJson( - json.decode('''{ - "id": 1, - "number": $prNumber, - "state": "$state", - "title": ${jsonEncode(title)}, - "user": { - "login": "$author", - "id": 1 - }, - "body": "$body", - "labels": [ - { - "id": 487496476, - "name": "$labelName" - }, - { - "id": 284437560, - "name": "$autosubmitLabel" - } - ], - "created_at": "2011-01-26T19:01:12Z", - "merged_at": "${mergedAt ?? DateTime.now().subtract(const Duration(hours: 12))}", - "closed_at": "2011-01-26T19:10:12Z", - "head": { - "label": "octocat:new-topic", - "ref": "new-topic", - "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e", - "repo": { - "id": 1296269, - "name": "Hello-World", - "full_name": "octocat/Hello-World", - "owner": { - "login": "octocat", - "id": 1, - "avatar_url": "https://github.com/images/error/octocat_happy.gif", - "html_url": "https://github.com/octocat" - } - } - }, - "base": { - "label": "octocat:master", - "label": "octocat:main", - "ref": "$baseRef", - "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e", - "repo": { - "id": 1296269, - "name": "$repoName", - "full_name": "$login/$repoName", - "owner": { - "login": "$login", - "id": 1, - "avatar_url": "https://github.com/images/error/octocat_happy.gif", - "html_url": "https://github.com/octocat" - } - } - }, - "author_association": "$authorAssociation", - "mergeable": $mergeable, - "mergeable_state": "clean" - }''') as Map, - ); -} - -const String reviewsMock = '''[ - { - "id": 80, - "user": { - "login": "octocat2", - "id": 1 - }, - "body": "Here is the body for the review.", - "state": "APPROVED", - "author_association": "OWNER" - } -]'''; - -const String unApprovedReviewsMock = '''[ - { - "id": 81, - "user": { - "login": "octocat", - "id": 1 - }, - "body": "Here is the body for the review.", - "state": "CHANGES_REQUESTED", - "author_association": "OWNER" - } -]'''; - -const String checkRunsMock = '''{ - "total_count": 1, - "check_runs": [ - { - "id": 1, - "head_sha": "be6ff099a4ee56e152a5fa2f37edd10f79d1269a", - "external_id": "", - "details_url": "https://example.com", - "status": "completed", - "conclusion": "success", - "started_at": "2018-05-04T01:14:52Z", - "name": "mighty_readme", - "check_suite": { - "id": 5 - } - } - ] -}'''; - -const String failedCheckRunsMock = '''{ - "total_count": 1, - "check_runs": [ - { - "id": 2, - "head_sha": "be6ff099a4ee56e152a5fa2f37edd10f79d1269a", - "external_id": "", - "details_url": "https://example.com", - "status": "completed", - "conclusion": "failure", - "started_at": "2018-05-04T01:14:52Z", - "name": "failed_checkrun", - "check_suite": { - "id": 5 - } - } - ] -}'''; - -const String neutralCheckRunsMock = '''{ - "total_count": 1, - "check_runs": [ - { - "id": 2, - "head_sha": "be6ff099a4ee56e152a5fa2f37edd10f79d1269a", - "external_id": "", - "details_url": "https://example.com", - "status": "completed", - "conclusion": "neutral", - "started_at": "2018-05-04T01:14:52Z", - "name": "neutral_checkrun", - "check_suite": { - "id": 5 - } - } - ] -}'''; - -const String inProgressCheckRunsMock = '''{ - "total_count": 1, - "check_runs": [ - { - "id": 3, - "head_sha": "be6ff099a4ee56e152a5fa2f37edd10f79d1269a", - "external_id": "", - "details_url": "https://example.com", - "status": "in_progress", - "conclusion": "neutral", - "started_at": "2018-05-04T01:14:52Z", - "name": "inprogress_checkrun", - "check_suite": { - "id": 5 - } - } - ] -}'''; - -const String skippedCheckRunsMock = '''{ - "total_count": 1, - "check_runs": [ - { - "id": 6, - "head_sha": "be6ff099a4ee56e152a5fa2f37edd10f79d1269a", - "external_id": "", - "details_url": "https://example.com", - "status": "in_progress", - "conclusion": "skipped", - "started_at": "2018-05-04T01:14:52Z", - "name": "inprogress_checkrun", - "check_suite": { - "id": 5 - } - } - ] -}'''; - -const String multipleCheckRunsMock = '''{ - "total_count": 3, - "check_runs": [ - { - "id": 1, - "head_sha": "be6ff099a4ee56e152a5fa2f37edd10f79d1269a", - "external_id": "", - "details_url": "https://example.com", - "status": "completed", - "conclusion": "success", - "started_at": "2018-05-04T01:14:52Z", - "name": "mighty_readme", - "check_suite": { - "id": 5 - } - }, - { - "id": 2, - "head_sha": "be6ff099a4ee56e152a5fa2f37edd10f79d1269a", - "external_id": "", - "details_url": "https://example.com", - "status": "completed", - "conclusion": "neutral", - "started_at": "2018-05-04T01:14:52Z", - "name": "neutral_checkrun", - "check_suite": { - "id": 5 - } - }, - { - "id": 6, - "head_sha": "be6ff099a4ee56e152a5fa2f37edd10f79d1269a", - "external_id": "", - "details_url": "https://example.com", - "status": "in_progress", - "conclusion": "skipped", - "started_at": "2018-05-04T01:14:52Z", - "name": "inprogress_checkrun", - "check_suite": { - "id": 5 - } - } - ] -}'''; - -const String multipleCheckRunsWithFailureMock = '''{ - "total_count": 3, - "check_runs": [ - { - "id": 1, - "head_sha": "be6ff099a4ee56e152a5fa2f37edd10f79d1269a", - "external_id": "", - "details_url": "https://example.com", - "status": "completed", - "conclusion": "success", - "started_at": "2018-05-04T01:14:52Z", - "name": "mighty_readme", - "check_suite": { - "id": 5 - } - }, - { - "id": 2, - "head_sha": "be6ff099a4ee56e152a5fa2f37edd10f79d1269a", - "external_id": "", - "details_url": "https://example.com", - "status": "completed", - "conclusion": "failure", - "started_at": "2018-05-04T01:14:52Z", - "name": "failed_checkrun", - "check_suite": { - "id": 5 - } - }, - { - "id": 6, - "head_sha": "be6ff099a4ee56e152a5fa2f37edd10f79d1269a", - "external_id": "", - "details_url": "https://example.com", - "status": "in_progress", - "conclusion": "skipped", - "started_at": "2018-05-04T01:14:52Z", - "name": "inprogress_checkrun", - "check_suite": { - "id": 5 - } - } - ] -}'''; - -const String inprogressAndNotFailedCheckRunMock = '''{ - "total_count": 1, - "check_runs": [ - { - "id": 6, - "head_sha": "be6ff099a4ee56e152a5fa2f37edd10f79d1269a", - "external_id": "", - "details_url": "https://example.com", - "status": "in_progress", - "conclusion": "neutral", - "started_at": "2018-05-04T01:14:52Z", - "name": "inprogress_checkrun", - "check_suite": { - "id": 5 - } - } - ] -}'''; - -const String emptyCheckRunsMock = '''{"check_runs": [{}]}'''; - -// repositoryStatusesMock is from the official Github API: https://developer.github.com/v3/repos/statuses/#list-statuses-for-a-specific-ref -// state can be error, failure, pending, success -const String repositoryStatusesMock = '''{ - "state": "success", - "statuses": [ - { - "state": "success", - "context": "tree-status" - }, - { - "state": "success", - "context": "tree-status/flutter" - } - ] -}'''; - -const String repositoryStatusesWithGoldMock = '''{ - "state": "success", - "statuses": [ - { - "state": "success", - "context": "tree-status" - }, - { - "state": "PENDING", - "context": "flutter-gold" - } - ] -}'''; - -const String repositoryStatusesWithFailedGoldMock = '''{ - "state": "success", - "statuses": [ - { - "state": "success", - "context": "tree-status" - }, - { - "state": "failure", - "context": "flutter-gold", - "targetUrl": "https://ci.example.com/1000/output" - } - ] -}'''; - -const String repositoryStatusesNonLuciFlutterMock = '''{ - "state": "success", - "statuses": [ - { - "state": "success", - "context": "infra" - }, - { - "state": "success", - "context": "config" - } - ] -}'''; - -const String failedAuthorsStatusesMock = '''{ - "state": "failure", - "statuses": [ - { - "state": "failure", - "context": "tree-status", - "targetUrl": "https://ci.example.com/1000/output" - }, - { - "state": "failure", - "context": "tree-status", - "targetUrl": "https://ci.example.com/2000/output" - } - ] -}'''; - -const String failedNonAuthorsStatusesMock = '''{ - "state": "failure", - "statuses": [ - { - "state": "failure", - "context": "flutter-engine", - "targetUrl": "https://ci.example.com/1000/output" - }, - { - "state": "failure", - "context": "flutter-infra", - "targetUrl": "https://ci.example.com/2000/output" - } - ] -}'''; - -const String emptyStatusesMock = '''{"statuses": [{}]}'''; - -// commitMock is from the official Github API: https://docs.github.com/en/rest/reference/commits#get-a-commit -const String commitMock = '''{ - "sha": "HEAD~", - "commit": { - "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/6dcb09b5b57875f334f61aebed695e2e4193db5e", - "message": "Fix all the bugs" - } -}'''; - -const String shouldRebaseMock = '''{ - "url": "https://api.github.com/repos/octocat/Hello-World/compare/master...topic", - "status": "behind", - "ahead_by": 1, - "behind_by": 11, - "total_commits": 1, - "files": [ - { - "sha": "bbcd538c8e72b8c175046e27cc8f907076331401", - "filename": "file1.txt", - "status": "added", - "changes": 124 - } - ] -}'''; - -// compareTwoCommitsMock is from the official Github API: https://docs.github.com/en/rest/reference/commits#compare-two-commits -const String compareTwoCommitsMock = '''{ - "url": "https://api.github.com/repos/octocat/Hello-World/compare/master...topic", - "status": "behind", - "ahead_by": 1, - "behind_by": 2, - "total_commits": 1, - "files": [ - { - "sha": "bbcd538c8e72b8c175046e27cc8f907076331401", - "filename": "file1.txt", - "status": "added", - "changes": 124 - } - ] -}'''; - -const String compareToTCommitsMock = '''{ - "url": "https://api.github.com/repos/octocat/Hello-World/compare/master...topic", - "status": "behind", - "ahead_by": 1, - "behind_by": 2, - "total_commits": 1, - "files": [] -}'''; - -// successMergeMock is from the offcial github API: https://docs.github.com/en/rest/reference/pulls#merge-a-pull-request. -const String successMergeMock = ''' -{ - "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e", - "merged": true -}'''; - -// createCommentMock is from the offcial github API: https://docs.github.com/en/rest/reference/pulls#create-a-review-comment-for-a-pull-request. -const String createCommentMock = ''' -{ - "id": 10, - "position": 1, - "commit_id": "6dcb09b5b57875f334f61aebed695e2e4193db5e", - "user": { - "login": "octocat", - "id": 1 - }, - "body": "Great stuff!" -}'''; - -const String pullRequestMergeMock = ''' -{ - "sha": "6dcb09b5b57875f334f61aebed695e2e4193db5e", - "merged": true -}'''; diff --git a/auto_submit/test/service/approver_service_test.dart b/auto_submit/test/service/approver_service_test.dart deleted file mode 100644 index 003f74357..000000000 --- a/auto_submit/test/service/approver_service_test.dart +++ /dev/null @@ -1,56 +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. - -import 'package:auto_submit/configuration/repository_configuration.dart'; -import 'package:auto_submit/service/approver_service.dart'; -import 'package:github/github.dart' as gh; -import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; - -import '../configuration/repository_configuration_data.dart'; -import '../requests/github_webhook_test_data.dart'; -import '../src/service/fake_config.dart'; -import '../utilities/mocks.dart'; - -void main() { - FakeConfig config; - late ApproverService service; - late MockGitHub github; - late MockPullRequestsService pullRequests; - - setUp(() { - github = MockGitHub(); - config = FakeConfig(githubClient: github); - config.repositoryConfigurationMock = RepositoryConfiguration.fromYaml(sampleConfigNoOverride); - service = ApproverService(config); - pullRequests = MockPullRequestsService(); - when(github.pullRequests).thenReturn(pullRequests); - when(pullRequests.createReview(any, any)).thenAnswer((_) async => gh.PullRequestReview(id: 123, user: gh.User())); - }); - - test('Verify approval ignored', () async { - final gh.PullRequest pr = generatePullRequest(author: 'not_a_user'); - await service.autoApproval(pr); - verifyNever(pullRequests.createReview(any, captureAny)); - }); - - test('Verify approve', () async { - when(pullRequests.listReviews(any, any)).thenAnswer((_) => const Stream.empty()); - final gh.PullRequest pr = generatePullRequest(author: 'dependabot[bot]'); - await service.autoApproval(pr); - final List reviews = verify(pullRequests.createReview(any, captureAny)).captured; - expect(reviews.length, 1); - final gh.CreatePullRequestReview review = reviews.single as gh.CreatePullRequestReview; - expect(review.event, 'APPROVE'); - }); - - test('Already approved', () async { - final gh.PullRequestReview review = - gh.PullRequestReview(id: 123, user: gh.User(login: 'fluttergithubbot'), state: 'APPROVED'); - when(pullRequests.listReviews(any, any)).thenAnswer((_) => Stream.value(review)); - final gh.PullRequest pr = generatePullRequest(author: 'dependabot[bot]'); - await service.autoApproval(pr); - verifyNever(pullRequests.createReview(any, captureAny)); - }); -} diff --git a/auto_submit/test/service/bigquery_test.dart b/auto_submit/test/service/bigquery_test.dart deleted file mode 100644 index ae67c4cb6..000000000 --- a/auto_submit/test/service/bigquery_test.dart +++ /dev/null @@ -1,591 +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. - -import 'dart:convert'; - -import 'package:auto_submit/exception/bigquery_exception.dart'; -import 'package:auto_submit/model/big_query_pull_request_record.dart'; -import 'package:googleapis/bigquery/v2.dart'; -import 'package:mockito/mockito.dart'; -import 'package:test/expect.dart'; -import 'package:test/scaffolding.dart'; - -import '../src/service/fake_bigquery_service.dart'; -import '../utilities/mocks.dart'; - -const String revertRequestRecordResponse = ''' -{ - "jobComplete": true, - "rows": [ - { "f": [ - { "v": "flutter"}, - { "v": "cocoon" }, - { "v": "ricardoamador" }, - { "v": "1024" }, - { "v": "123f124" }, - { "v": "123456789" }, - { "v": "123456999" }, - { "v": "ricardoamador" }, - { "v": "2048" }, - { "v": "ce345dc" }, - { "v": "234567890" }, - { "v": "234567999" }, - { "v": "ricardoamador" }, - { "v": "11304" }, - { "v": "1640979000000" }, - { "v": "0" }, - { "v": "" } - ] - } - ] -} -'''; - -const String pullRequestRecordResponse = ''' -{ - "jobComplete": true, - "rows": [ - { "f": [ - { "v": "123456789"}, - { "v": "234567890" }, - { "v": "flutter" }, - { "v": "cocoon" }, - { "v": "ricardoamador" }, - { "v": "345" }, - { "v": "ade456" }, - { "v": "merge" } - ] - } - ] -} -'''; - -const String successResponseNoRowsAffected = ''' -{ - "jobComplete": true -} -'''; - -const String insertDeleteUpdateSuccessResponse = ''' -{ - "jobComplete": true, - "numDmlAffectedRows": "1" -} -'''; - -const String insertDeleteUpdateSuccessTooManyRows = ''' -{ - "jobComplete": true, - "numDmlAffectedRows": "2" -} -'''; - -const String selectPullRequestTooManyRowsResponse = ''' -{ - "jobComplete": true, - "numDmlAffectedRows": "2", - "rows": [ - { "f": [ - { "v": "123456789"}, - { "v": "234567890" }, - { "v": "flutter" }, - { "v": "cocoon" }, - { "v": "ricardoamador" }, - { "v": "345" }, - { "v": "ade456" }, - { "v": "merge" } - ] - }, - { "f": [ - { "v": "123456789"}, - { "v": "234567890" }, - { "v": "flutter" }, - { "v": "cocoon" }, - { "v": "ricardoamador" }, - { "v": "345" }, - { "v": "ade456" }, - { "v": "merge" } - ] - } - ] -} -'''; - -const String selectRevertRequestTooManyRowsResponse = ''' -{ - "jobComplete": true, - "numDmlAffectedRows": "2", - "rows": [ - { "f": [ - { "v": "flutter"}, - { "v": "cocoon" }, - { "v": "ricardoamador" }, - { "v": "1024" }, - { "v": "123f124" }, - { "v": "123456789" }, - { "v": "123456999" }, - { "v": "ricardoamador" }, - { "v": "2048" }, - { "v": "ce345dc" }, - { "v": "234567890" }, - { "v": "234567999" } - ] - }, - { "f": [ - { "v": "flutter"}, - { "v": "cocoon" }, - { "v": "ricardoamador" }, - { "v": "1024" }, - { "v": "123f124" }, - { "v": "123456789" }, - { "v": "123456999" }, - { "v": "ricardoamador" }, - { "v": "2048" }, - { "v": "ce345dc" }, - { "v": "234567890" }, - { "v": "234567999" } - ] - } - ] -} -'''; - -const String errorResponse = ''' -{ - "jobComplete": false -} -'''; - -const String selectReviewRequestRecordsResponse = ''' -{ - "jobComplete": true, - "numDmlAffectedRows": "2", - "rows": [ - { "f": [ - { "v": "Keyonghan" }, - { "v": "2048" }, - { "v": "234567890" }, - { "v": "0" }, - { "v": "" } - ] - }, - { "f": [ - { "v": "caseyhillers" }, - { "v": "2049" }, - { "v": "234567890" }, - { "v": "0" }, - { "v": "" } - ] - } - ] -} -'''; - -const String expectedProjectId = 'flutter-dashboard'; - -void main() { - late FakeBigqueryService service; - late MockJobsResource jobsResource; - - setUp(() { - jobsResource = MockJobsResource(); - service = FakeBigqueryService(jobsResource); - }); - - test('Insert pull request record is successful.', () async { - when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) { - return Future.value( - QueryResponse.fromJson(jsonDecode(insertDeleteUpdateSuccessResponse) as Map), - ); - }); - - final PullRequestRecord pullRequestRecord = PullRequestRecord( - prCreatedTimestamp: DateTime.fromMillisecondsSinceEpoch(123456789), - prLandedTimestamp: DateTime.fromMillisecondsSinceEpoch(234567890), - organization: 'flutter', - repository: 'cocoon', - author: 'ricardoamador', - prNumber: 345, - prCommit: 'ade456', - prRequestType: 'merge', - ); - - bool hasError = false; - try { - await service.insertPullRequestRecord( - projectId: expectedProjectId, - pullRequestRecord: pullRequestRecord, - ); - } on BigQueryException { - hasError = true; - } - expect(hasError, isFalse); - }); - - test('Insert pull request record handles unsuccessful job complete error.', () async { - when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) { - return Future.value( - QueryResponse.fromJson(jsonDecode(errorResponse) as Map), - ); - }); - - bool hasError = false; - final PullRequestRecord pullRequestRecord = PullRequestRecord( - prCreatedTimestamp: DateTime.fromMillisecondsSinceEpoch(123456789), - prLandedTimestamp: DateTime.fromMillisecondsSinceEpoch(234567890), - organization: 'flutter', - repository: 'cocoon', - author: 'ricardoamador', - prNumber: 345, - prCommit: 'ade456', - prRequestType: 'merge', - ); - - try { - await service.insertPullRequestRecord( - projectId: expectedProjectId, - pullRequestRecord: pullRequestRecord, - ); - } on BigQueryException catch (exception) { - expect(exception.cause, 'Insert pull request $pullRequestRecord did not complete.'); - hasError = true; - } - expect(hasError, isTrue); - }); - - test('Insert pull request fails when multiple rows are returned.', () async { - when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) { - return Future.value( - QueryResponse.fromJson(jsonDecode(selectPullRequestTooManyRowsResponse) as Map), - ); - }); - - bool hasError = false; - final PullRequestRecord pullRequestRecord = PullRequestRecord( - prCreatedTimestamp: DateTime.fromMillisecondsSinceEpoch(123456789), - prLandedTimestamp: DateTime.fromMillisecondsSinceEpoch(234567890), - organization: 'flutter', - repository: 'cocoon', - author: 'ricardoamador', - prNumber: 345, - prCommit: 'ade456', - prRequestType: 'merge', - ); - - try { - await service.insertPullRequestRecord( - projectId: expectedProjectId, - pullRequestRecord: pullRequestRecord, - ); - } on BigQueryException catch (exception) { - expect(exception.cause, 'There was an error inserting $pullRequestRecord into the table.'); - hasError = true; - } - expect(hasError, isTrue); - }); - - test('Select pull request is successful.', () async { - when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) { - return Future.value( - QueryResponse.fromJson(jsonDecode(pullRequestRecordResponse) as Map), - ); - }); - - final PullRequestRecord pullRequestRecord = await service.selectPullRequestRecordByPrNumber( - projectId: expectedProjectId, - prNumber: 345, - repository: 'cocoon', - ); - - expect(pullRequestRecord, isNotNull); - expect(pullRequestRecord.prCreatedTimestamp, equals(DateTime.fromMillisecondsSinceEpoch(123456789))); - expect(pullRequestRecord.prLandedTimestamp, equals(DateTime.fromMillisecondsSinceEpoch(234567890))); - expect(pullRequestRecord.organization, equals('flutter')); - expect(pullRequestRecord.repository, equals('cocoon')); - expect(pullRequestRecord.author, equals('ricardoamador')); - expect(pullRequestRecord.prNumber, 345); - expect(pullRequestRecord.prCommit, equals('ade456')); - expect(pullRequestRecord.prRequestType, equals('merge')); - }); - - test('Select pull request handles unsuccessful job failure.', () async { - when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) { - return Future.value(QueryResponse.fromJson(jsonDecode(errorResponse) as Map)); - }); - - bool hasError = false; - try { - await service.selectPullRequestRecordByPrNumber( - projectId: expectedProjectId, - prNumber: 345, - repository: 'cocoon', - ); - } on BigQueryException catch (exception) { - hasError = true; - expect(exception.cause, 'Get pull request by pr# 345 in repository cocoon did not complete.'); - } - expect(hasError, isTrue); - }); - - test('Select pull request handles no rows returned failure.', () async { - when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) { - return Future.value( - QueryResponse.fromJson(jsonDecode(successResponseNoRowsAffected) as Map), - ); - }); - - bool hasError = false; - try { - await service.selectPullRequestRecordByPrNumber( - projectId: expectedProjectId, - prNumber: 345, - repository: 'cocoon', - ); - } on BigQueryException catch (exception) { - hasError = true; - expect( - exception.cause, - 'Could not find an entry for pull request with pr# 345 in repository cocoon.', - ); - } - expect(hasError, isTrue); - }); - - test('Select pull request handles too many rows returned failure.', () async { - when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) { - return Future.value( - QueryResponse.fromJson(jsonDecode(selectPullRequestTooManyRowsResponse) as Map), - ); - }); - - bool hasError = false; - try { - await service.selectPullRequestRecordByPrNumber( - projectId: expectedProjectId, - prNumber: 345, - repository: 'cocoon', - ); - } on BigQueryException catch (exception) { - hasError = true; - expect( - exception.cause, - 'More than one record was returned for pull request with pr# 345 in repository cocoon.', - ); - } - expect(hasError, isTrue); - }); - - test('Delete pull request record handles failure to complete job.', () async { - when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) { - return Future.value(QueryResponse.fromJson(jsonDecode(errorResponse) as Map)); - }); - - bool hasError = false; - try { - await service.deletePullRequestRecord( - projectId: expectedProjectId, - prNumber: 345, - repository: 'cocoon', - ); - } on BigQueryException catch (exception) { - hasError = true; - expect( - exception.cause, - 'Delete pull request with pr# 345 in repository cocoon did not complete.', - ); - } - expect(hasError, isTrue); - }); - - test('Delete pull request record handles success but no affected rows.', () async { - when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) { - return Future.value( - QueryResponse.fromJson(jsonDecode(successResponseNoRowsAffected) as Map), - ); - }); - - bool hasError = false; - try { - await service.deletePullRequestRecord( - projectId: expectedProjectId, - prNumber: 345, - repository: 'cocoon', - ); - } on BigQueryException catch (exception) { - hasError = true; - expect( - exception.cause, - 'Could not find pull request with pr# 345 in repository cocoon to delete.', - ); - } - expect(hasError, isTrue); - }); - - test('Delete pull request record handles success but wrong number of affected rows.', () async { - when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) { - return Future.value( - QueryResponse.fromJson(jsonDecode(insertDeleteUpdateSuccessTooManyRows) as Map), - ); - }); - - bool hasError = false; - try { - await service.deletePullRequestRecord( - projectId: expectedProjectId, - prNumber: 345, - repository: 'cocoon', - ); - } on BigQueryException catch (exception) { - hasError = true; - expect( - exception.cause, - 'More than one row was deleted from the database for pull request with pr# 345 in repository cocoon.', - ); - } - expect(hasError, isTrue); - }); - - test('Delete revert request record handles failure to complete job.', () async { - when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) { - return Future.value( - QueryResponse.fromJson(jsonDecode(errorResponse) as Map), - ); - }); - - bool hasError = false; - try { - await service.deleteRevertRequestRecord( - projectId: expectedProjectId, - prNumber: 2048, - repository: 'cocoon', - ); - } on BigQueryException catch (exception) { - hasError = true; - expect( - exception.cause, - 'Delete revert request with pr# 2048 in repository cocoon did not complete.', - ); - } - expect(hasError, isTrue); - }); - - test('Delete revert request record handles success but no affected rows.', () async { - when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) { - return Future.value( - QueryResponse.fromJson(jsonDecode(successResponseNoRowsAffected) as Map), - ); - }); - - bool hasError = false; - try { - await service.deleteRevertRequestRecord( - projectId: expectedProjectId, - prNumber: 2048, - repository: 'cocoon', - ); - } on BigQueryException catch (exception) { - hasError = true; - expect( - exception.cause, - 'Could not find revert request with pr# 2048 in repository cocoon to delete.', - ); - } - expect(hasError, isTrue); - }); - - test('Delete revert request record handles success but wrong number of affected rows.', () async { - when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) { - return Future.value( - QueryResponse.fromJson(jsonDecode(insertDeleteUpdateSuccessTooManyRows) as Map), - ); - }); - - bool hasError = false; - try { - await service.deleteRevertRequestRecord( - projectId: expectedProjectId, - prNumber: 2048, - repository: 'cocoon', - ); - } on BigQueryException catch (exception) { - hasError = true; - expect( - exception.cause, - 'More than one row was deleted from the database for revert request with pr# 2048 in repository cocoon.', - ); - } - expect(hasError, isTrue); - }); - - test('Update record is successful.', () async { - when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) { - return Future.value( - QueryResponse.fromJson(jsonDecode(insertDeleteUpdateSuccessResponse) as Map), - ); - }); - - bool hasError = false; - try { - await service.updateReviewRequestIssue( - projectId: expectedProjectId, - reviewIssueLandedTimestamp: DateTime.now(), - reviewIssueNumber: 2048, - reviewIssueClosedBy: 'ricardoamador', - ); - } on BigQueryException { - hasError = true; - } - - expect(hasError, isFalse); - }); - - test('Update revert request review record is successful but wrong number of rows is updated.', () async { - when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) { - return Future.value( - QueryResponse.fromJson(jsonDecode(insertDeleteUpdateSuccessTooManyRows) as Map), - ); - }); - - bool hasError = false; - try { - await service.updateReviewRequestIssue( - projectId: expectedProjectId, - reviewIssueLandedTimestamp: DateTime.now(), - reviewIssueNumber: 2048, - reviewIssueClosedBy: 'ricardoamador', - ); - } on BigQueryException catch (exception) { - hasError = true; - expect( - exception.cause, - 'There was an error updating revert request record review issue landed timestamp with review issue number 2048.', - ); - } - - expect(hasError, isTrue); - }); - - test('Update revert request review record does not complete successfully.', () async { - when(jobsResource.query(captureAny, expectedProjectId)).thenAnswer((Invocation invocation) { - return Future.value( - QueryResponse.fromJson(jsonDecode(errorResponse) as Map), - ); - }); - - bool hasError = false; - try { - await service.updateReviewRequestIssue( - projectId: expectedProjectId, - reviewIssueLandedTimestamp: DateTime.now(), - reviewIssueNumber: 2048, - reviewIssueClosedBy: 'ricardoamador', - ); - } on BigQueryException catch (exception) { - hasError = true; - expect(exception.cause, 'Update of review issue 2048 did not complete.'); - } - - expect(hasError, isTrue); - }); -} diff --git a/auto_submit/test/service/config_test.dart b/auto_submit/test/service/config_test.dart deleted file mode 100644 index 88eed7ed9..000000000 --- a/auto_submit/test/service/config_test.dart +++ /dev/null @@ -1,86 +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:io'; -import 'dart:typed_data'; -import 'dart:convert'; - -import 'package:auto_submit/service/config.dart'; -import 'package:auto_submit/service/secrets.dart'; -import 'package:github/github.dart'; -import 'package:http/testing.dart'; -import 'package:http/http.dart' as http; -import 'package:neat_cache/cache_provider.dart'; -import 'package:neat_cache/neat_cache.dart'; -import 'package:test/test.dart'; - -import 'config_test_data.dart'; - -/// Number of entries allowed in [Cache]. -const int kCacheSize = 1024; - -void main() { - group('Config', () { - late CacheProvider cacheProvider; - late Config config; - late MockClient mockClient; - final SecretManager secretManager = LocalSecretManager(); - final RepositorySlug flutterSlug = RepositorySlug('flutter', 'flutter'); - final RepositorySlug testSlug = RepositorySlug('test', 'test'); - const int kCacheSize = 1024; - - setUp(() { - cacheProvider = Cache.inMemoryCacheProvider(kCacheSize); - mockClient = MockClient((_) async => http.Response(installations, HttpStatus.ok)); - config = Config( - cacheProvider: cacheProvider, - httpProvider: () => mockClient, - secretManager: secretManager, - ); - }); - - test('verify github App Installation Id ', () async { - final Uri githubInstallationUri = Uri.https('api.github.com', 'app/installations'); - final http.Response response = await mockClient.get(githubInstallationUri); - final List list = - (json.decode(response.body).map((dynamic data) => (data) as Map)).toList() as List; - expect(list[0]['id'].toString(), '24369313'); - expect(list[1]['id'].toString(), '23587612'); - }); - - test('generateGithubToken pulls from cache', () async { - const String configValue = 'githubToken'; - final Uint8List cachedValue = Uint8List.fromList(configValue.codeUnits); - final Cache cache = Cache(cacheProvider).withPrefix('config'); - await cache['githubToken-${flutterSlug.owner}'].set( - cachedValue, - const Duration(minutes: 1), - ); - - final String githubToken = await config.generateGithubToken(flutterSlug); - expect(githubToken, configValue); - }); - - test('Github clients are created with correct token', () async { - const String flutterToken = 'flutterToken'; - final Uint8List flutterValue = Uint8List.fromList(flutterToken.codeUnits); - const String testToken = 'testToken'; - final Uint8List testValue = Uint8List.fromList(testToken.codeUnits); - final Cache cache = Cache(cacheProvider).withPrefix('config'); - await cache['githubToken-${flutterSlug.owner}'].set( - flutterValue, - const Duration(minutes: 1), - ); - await cache['githubToken-${testSlug.owner}'].set( - testValue, - const Duration(minutes: 1), - ); - - final GitHub flutterClient = await config.createGithubClient(flutterSlug); - final GitHub testClient = await config.createGithubClient(testSlug); - expect(flutterClient.auth.token!, flutterToken); - expect(testClient.auth.token!, testToken); - }); - }); -} diff --git a/auto_submit/test/service/config_test_data.dart b/auto_submit/test/service/config_test_data.dart deleted file mode 100644 index c644a5015..000000000 --- a/auto_submit/test/service/config_test_data.dart +++ /dev/null @@ -1,69 +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. - -const String installations = ''' - [ - { - "id": 24369313, - "account": { - "login": "flutter", - "id": 14101776, - "node_id": "MDEyOk9yZ2FuaXphdGlvbjE0MTAxNzc2", - "avatar_url": "https://avatars.githubusercontent.com/u/14101776?v=4" - }, - "repository_selection": "selected", - "app_id": 168178, - "app_slug": "auto-submit", - "target_id": 14101776, - "target_type": "Organization", - "permissions": { - "checks": "read", - "metadata": "read", - "single_file": "read", - "pull_requests": "read" - }, - "events": [ - "label", - "pull_request" - ], - "created_at": "2022-03-23T23:00:37.000Z", - "updated_at": "2022-03-23T23:00:37.000Z", - "single_file_name": ".github/autosubmit.yml", - "has_multiple_single_files": false, - "single_file_paths": [ - ".github/autosubmit.yml" - ] - }, - { - "id": 23587612, - "account": { - "login": "CaseyHillers", - "id": 2148558, - "node_id": "MDQ6VXNlcjIxNDg1NTg=", - "avatar_url": "https://avatars.githubusercontent.com/u/2148558?v=4" - }, - "repository_selection": "selected", - "app_id": 168178, - "app_slug": "auto-submit", - "target_id": 2148558, - "target_type": "User", - "permissions": { - "checks": "read", - "metadata": "read", - "single_file": "read", - "pull_requests": "read" - }, - "events": [ - "label", - "pull_request" - ], - "created_at": "2022-02-24T18:29:28.000Z", - "updated_at": "2022-03-23T22:18:46.000Z", - "single_file_name": ".github/autosubmit.yml", - "has_multiple_single_files": false, - "single_file_paths": [ - ".github/autosubmit.yml" - ] - } -]'''; diff --git a/auto_submit/test/service/github_service_test.dart b/auto_submit/test/service/github_service_test.dart deleted file mode 100644 index 6bfd46b28..000000000 --- a/auto_submit/test/service/github_service_test.dart +++ /dev/null @@ -1,74 +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. - -import 'dart:convert'; - -import 'package:auto_submit/service/github_service.dart'; -import 'package:github/github.dart'; -import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; - -import '../requests/github_webhook_test_data.dart'; -import '../utilities/mocks.dart'; - -void main() { - late GithubService githubService; - late RepositorySlug slug; - late RepositoryCommit testCommit; - final MockGitHub mockGitHub = MockGitHub(); - final MockRepositoriesService mockRepositoriesService = MockRepositoriesService(); - final MockGitHubComparison mockGitHubComparison = MockGitHubComparison(); - final MockResponse mockResponse = MockResponse(); - - const String author = '''{"login": "octocat", "id": 1}'''; - const String url = 'testUrl'; - const String sha = '6dcb09b5b57875f334f61aebed695e2e4193db5e'; - - setUp(() { - githubService = GithubService(mockGitHub); - slug = RepositorySlug('flutter', 'cocoon'); - testCommit = RepositoryCommit.fromJson( - jsonDecode('{"url": "$url", "author": $author, "sha": "$sha"}') as Map, - ); - - when(mockGitHubComparison.behindBy).thenReturn(10); - when( - mockGitHub.request( - any, - any, - headers: anyNamed('headers'), - params: anyNamed('params'), - body: anyNamed('body'), - statusCode: anyNamed('statusCode'), - fail: anyNamed('fail'), - preview: anyNamed('preview'), - ), - ).thenAnswer((_) => Future.value(mockResponse)); - when(mockResponse.statusCode).thenReturn(200); - when(mockGitHub.repositories).thenReturn(mockRepositoriesService); - when(mockRepositoriesService.getCommit(any, any)).thenAnswer((_) => Future.value(testCommit)); - when(mockRepositoriesService.compareCommits(any, any, any)).thenAnswer((_) => Future.value(mockGitHubComparison)); - }); - - test('listReviews retrieves all reviews of the pull request', () async { - final RepositoryCommit commit = await githubService.getCommit(slug, sha); - expect(commit.author!.login, 'octocat'); - expect(commit.url, 'testUrl'); - expect(commit.sha, '6dcb09b5b57875f334f61aebed695e2e4193db5e'); - }); - - test('Merges branch', () async { - when(mockGitHubComparison.behindBy).thenReturn(10); - final PullRequest pullRequest = generatePullRequest(); - await githubService.autoMergeBranch(pullRequest); - verify(mockResponse.statusCode).called(1); - }); - - test('Does not merge branch', () async { - when(mockGitHubComparison.behindBy).thenReturn(9); - final PullRequest pullRequest = generatePullRequest(); - await githubService.autoMergeBranch(pullRequest); - verifyNever(mockResponse.statusCode); - }); -} diff --git a/auto_submit/test/service/pull_request_validation_service_test.dart b/auto_submit/test/service/pull_request_validation_service_test.dart deleted file mode 100644 index 35eece32e..000000000 --- a/auto_submit/test/service/pull_request_validation_service_test.dart +++ /dev/null @@ -1,446 +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:async'; -import 'dart:convert'; - -import 'package:auto_submit/configuration/repository_configuration.dart'; -import 'package:auto_submit/model/auto_submit_query_result.dart' as auto hide PullRequest; -import 'package:auto_submit/service/pull_request_validation_service.dart'; -import 'package:auto_submit/service/validation_service.dart'; -import 'package:github/github.dart'; -import 'package:googleapis/bigquery/v2.dart'; -import 'package:mockito/mockito.dart'; -import 'package:retry/retry.dart'; -import 'package:test/test.dart'; - -import '../configuration/repository_configuration_data.dart'; -import '../requests/github_webhook_test_data.dart'; -import '../src/request_handling/fake_pubsub.dart'; -import '../src/service/fake_bigquery_service.dart'; -import '../src/service/fake_config.dart'; -import '../src/service/fake_graphql_client.dart'; -import '../src/service/fake_github_service.dart'; -import '../utilities/utils.dart'; -import '../utilities/mocks.dart'; -import 'bigquery_test.dart'; - -void main() { - late PullRequestValidationService validationService; - late FakeConfig config; - late FakeGithubService githubService; - late FakeGraphQLClient githubGraphQLClient; - late RepositorySlug slug; - - late MockJobsResource jobsResource; - late FakeBigqueryService bigqueryService; - - setUp(() { - githubGraphQLClient = FakeGraphQLClient(); - githubService = FakeGithubService(client: MockGitHub()); - config = FakeConfig(githubService: githubService, githubGraphQLClient: githubGraphQLClient); - validationService = PullRequestValidationService( - config, - retryOptions: const RetryOptions(delayFactor: Duration.zero, maxDelay: Duration.zero, maxAttempts: 1), - ); - slug = RepositorySlug('flutter', 'cocoon'); - - jobsResource = MockJobsResource(); - bigqueryService = FakeBigqueryService(jobsResource); - config.bigqueryService = bigqueryService; - config.repositoryConfigurationMock = RepositoryConfiguration.fromYaml(sampleConfigNoOverride); - - when(jobsResource.query(captureAny, any)).thenAnswer((Invocation invocation) { - return Future.value( - QueryResponse.fromJson(jsonDecode(insertDeleteUpdateSuccessResponse) as Map), - ); - }); - }); - - test('Leaves label and no comment when no approval if both parties are members', () async { - final PullRequestHelper flutterRequest = PullRequestHelper( - prNumber: 0, - lastCommitHash: oid, - reviews: [], - ); - githubService.checkRunsData = checkRunsMock; - githubService.createCommentData = createCommentMock; - githubService.isTeamMemberMockMap['author1'] = true; - githubService.isTeamMemberMockMap['member'] = true; - final FakePubSub pubsub = FakePubSub(); - final PullRequest pullRequest = generatePullRequest(prNumber: 0, repoName: slug.name); - githubService.pullRequestData = pullRequest; - unawaited(pubsub.publish('auto-submit-queue-sub', pullRequest)); - final auto.QueryResult queryResult = createQueryResult(flutterRequest); - - await validationService.processPullRequest( - config: config, - result: queryResult, - messagePullRequest: pullRequest, - ackId: 'test', - pubsub: pubsub, - ); - - expect(githubService.issueComment, isNull); - expect(githubService.labelRemoved, false); - assert(pubsub.messagesQueue.isNotEmpty); - }); - - // This tests for valid pull request into not default base branch which - // will ignore the tree status as it does not matter. - test('Processes successfully when base branch is not default', () async { - final PullRequestHelper flutterRequest = PullRequestHelper( - prNumber: 0, - lastCommitHash: oid, - reviews: [ - const PullRequestReviewHelper( - authorName: 'member', - state: ReviewState.APPROVED, - memberType: MemberType.OWNER, - ), - ], - ); - githubService.checkRunsData = checkRunsMock; - githubService.checkRunsMock = checkRunsMock; - githubService.createCommentData = createCommentMock; - githubService.isTeamMemberMockMap['author1'] = true; - githubService.isTeamMemberMockMap['member'] = true; - final FakePubSub pubsub = FakePubSub(); - final PullRequest pullRequest = generatePullRequest( - prNumber: 0, - repoName: slug.name, - baseRef: 'feature_a', - mergeable: true, - ); - unawaited(pubsub.publish('auto-submit-queue-sub', pullRequest)); - final auto.QueryResult queryResult = createQueryResult(flutterRequest); - githubService.pullRequestMock = pullRequest; - githubService.mergeRequestMock = PullRequestMerge( - merged: true, - sha: 'asdfioefmasdf', - message: 'Merged successfully.', - ); - - await validationService.processPullRequest( - config: config, - result: queryResult, - messagePullRequest: pullRequest, - ackId: 'test', - pubsub: pubsub, - ); - - // These checks indicate that the pull request has been merged as the label - // is not removed and there was no issue coment generated and the message - // was acknowledged. - expect(githubService.issueComment, isNull); - expect(githubService.labelRemoved, false); - assert(pubsub.messagesQueue.isEmpty); - }); - - // This tests for valid pull request where tree status was not ready for - // processing, meaning no issueComment was created and the 'autosubmit' label - // is not removed and we do not ack the message. - test('Processing fails when base branch is default with no statuses', () async { - final PullRequestHelper flutterRequest = PullRequestHelper( - prNumber: 0, - lastCommitHash: oid, - reviews: [ - const PullRequestReviewHelper( - authorName: 'member', - state: ReviewState.APPROVED, - memberType: MemberType.OWNER, - ), - ], - lastCommitStatuses: null, - ); - githubService.checkRunsData = checkRunsMock; - githubService.createCommentData = createCommentMock; - githubService.isTeamMemberMockMap['author1'] = true; - githubService.isTeamMemberMockMap['member'] = true; - final FakePubSub pubsub = FakePubSub(); - final PullRequest pullRequest = generatePullRequest(prNumber: 0); - githubService.pullRequestData = pullRequest; - unawaited(pubsub.publish('auto-submit-queue-sub', pullRequest)); - final auto.QueryResult queryResult = createQueryResult(flutterRequest); - - await validationService.processPullRequest( - config: config, - result: queryResult, - messagePullRequest: pullRequest, - ackId: 'test', - pubsub: pubsub, - ); - - expect(githubService.issueComment, isNull); - expect(githubService.labelRemoved, false); - assert(pubsub.messagesQueue.isNotEmpty); - }); - - group('Process pull request method tests', () { - test('Should process message when autosubmit label exists and pr is open', () async { - final PullRequest pullRequest = generatePullRequest(prNumber: 0, repoName: slug.name); - githubService.pullRequestData = pullRequest; - expect(await validationService.shouldProcess(pullRequest), true); - }); - - test('Skip processing message when autosubmit label does not exist anymore', () async { - final PullRequest pullRequest = generatePullRequest(prNumber: 0, repoName: slug.name); - pullRequest.labels = []; - githubService.pullRequestData = pullRequest; - expect(await validationService.shouldProcess(pullRequest), false); - }); - - test('Skip processing message when the pull request is closed', () async { - final PullRequest pullRequest = generatePullRequest(prNumber: 0, repoName: slug.name); - pullRequest.state = 'closed'; - githubService.pullRequestData = pullRequest; - expect(await validationService.shouldProcess(pullRequest), false); - }); - - test('Should not process message when revert label exists and pr is open', () async { - final PullRequest pullRequest = generatePullRequest(prNumber: 0, repoName: slug.name); - final IssueLabel issueLabel = IssueLabel(name: 'revert'); - pullRequest.labels = [issueLabel]; - githubService.pullRequestData = pullRequest; - expect(await validationService.shouldProcess(pullRequest), false); - }); - - test('Skip processing message when revert label exists and pr is closed', () async { - final PullRequest pullRequest = generatePullRequest(prNumber: 0, repoName: slug.name); - pullRequest.state = 'closed'; - final IssueLabel issueLabel = IssueLabel(name: 'revert'); - pullRequest.labels = [issueLabel]; - githubService.pullRequestData = pullRequest; - expect(await validationService.shouldProcess(pullRequest), false); - }); - }); - - group('processMerge', () { - test('Correct PR titles when merging to use Reland', () async { - final PullRequest pullRequest = generatePullRequest( - prNumber: 0, - repoName: slug.name, - title: 'Revert "Revert "My first PR!"', - mergeable: true, - ); - githubService.pullRequestData = pullRequest; - githubService.mergeRequestMock = PullRequestMerge( - merged: true, - sha: pullRequest.mergeCommitSha, - ); - - final MergeResult result = await validationService.processMerge( - config: config, - messagePullRequest: pullRequest, - ); - - expect(result.message, contains('Reland "My first PR!"')); - }); - - test('Removes label and post comment when no approval for non-flutter hacker', () async { - final PullRequestHelper flutterRequest = PullRequestHelper( - prNumber: 0, - lastCommitHash: oid, - reviews: [], - ); - githubService.checkRunsData = checkRunsMock; - githubService.createCommentData = createCommentMock; - githubService.isTeamMemberMockMap['author1'] = false; - githubService.isTeamMemberMockMap['member'] = true; - final FakePubSub pubsub = FakePubSub(); - final PullRequest pullRequest = generatePullRequest(prNumber: 0, repoName: slug.name); - githubService.pullRequestData = pullRequest; - unawaited(pubsub.publish('auto-submit-queue-sub', pullRequest)); - final auto.QueryResult queryResult = createQueryResult(flutterRequest); - - await validationService.processPullRequest( - config: config, - result: queryResult, - messagePullRequest: pullRequest, - ackId: 'test', - pubsub: pubsub, - ); - - expect(githubService.issueComment, isNotNull); - expect(githubService.labelRemoved, true); - assert(pubsub.messagesQueue.isEmpty); - }); - - // This tests for valid pull request where tree status was not ready for - // processing, meaning no issueComment was created and the 'autosubmit' label - // is not removed and we do not ack the message. - test('Processing fails when base branch is default with no statuses', () async { - final PullRequestHelper flutterRequest = PullRequestHelper( - prNumber: 0, - lastCommitHash: oid, - reviews: [ - const PullRequestReviewHelper( - authorName: 'member', - state: ReviewState.APPROVED, - memberType: MemberType.OWNER, - ), - ], - lastCommitStatuses: null, - ); - githubService.checkRunsData = checkRunsMock; - githubService.createCommentData = createCommentMock; - githubService.isTeamMemberMockMap['author1'] = true; - githubService.isTeamMemberMockMap['member'] = true; - final FakePubSub pubsub = FakePubSub(); - final PullRequest pullRequest = generatePullRequest(prNumber: 0); - githubService.pullRequestData = pullRequest; - unawaited(pubsub.publish('auto-submit-queue-sub', pullRequest)); - final auto.QueryResult queryResult = createQueryResult(flutterRequest); - - await validationService.processPullRequest( - config: config, - result: queryResult, - messagePullRequest: pullRequest, - ackId: 'test', - pubsub: pubsub, - ); - - expect(githubService.issueComment, isNull); - expect(githubService.labelRemoved, false); - assert(pubsub.messagesQueue.isNotEmpty); - }); - - test('Processes successfully when base branch is not default', () async { - final PullRequestHelper flutterRequest = PullRequestHelper( - prNumber: 0, - lastCommitHash: oid, - reviews: [ - const PullRequestReviewHelper( - authorName: 'member', - state: ReviewState.APPROVED, - memberType: MemberType.OWNER, - ), - ], - ); - githubService.checkRunsData = checkRunsMock; - githubService.checkRunsMock = checkRunsMock; - githubService.createCommentData = createCommentMock; - githubService.isTeamMemberMockMap['author1'] = true; - githubService.isTeamMemberMockMap['member'] = true; - final FakePubSub pubsub = FakePubSub(); - final PullRequest pullRequest = generatePullRequest( - prNumber: 0, - repoName: slug.name, - baseRef: 'feature_a', - mergeable: true, - ); - unawaited(pubsub.publish('auto-submit-queue-sub', pullRequest)); - final auto.QueryResult queryResult = createQueryResult(flutterRequest); - githubService.pullRequestMock = pullRequest; - githubService.mergeRequestMock = PullRequestMerge( - merged: true, - sha: 'asdfioefmasdf', - message: 'Merged successfully.', - ); - - await validationService.processPullRequest( - config: config, - result: queryResult, - messagePullRequest: pullRequest, - ackId: 'test', - pubsub: pubsub, - ); - - // These checks indicate that the pull request has been merged as the label - // is not removed and there was no issue comment generated and the message - // was acknowledged. - expect(githubService.issueComment, isNull); - expect(githubService.labelRemoved, false); - assert(pubsub.messagesQueue.isEmpty); - }); - - test('includes PR description in commit message', () async { - final PullRequest pullRequest = generatePullRequest( - prNumber: 0, - repoName: slug.name, - title: 'PR title', - // The test-only helper function `generatePullRequest` will interpolate - // this string into a JSON string which will then be decoded--thus, this string must be - // a valid JSON substring, with escaped newlines. - body: r'PR description\nwhich\nis multiline.', - mergeable: true, - ); - githubService.pullRequestData = pullRequest; - githubService.mergeRequestMock = PullRequestMerge( - merged: true, - sha: pullRequest.mergeCommitSha, - ); - final MergeResult result = await validationService.processMerge( - config: config, - messagePullRequest: pullRequest, - ); - - expect(result.message, ''' -PR description -which -is multiline.'''); - }); - - test('commit message filters out markdown checkboxes', () async { - const String prTitle = 'Important update #4'; - const String prBody = ''' -Various bugfixes and performance improvements. - -Fixes #12345 and #3. -This is the second line in a paragraph. - -## Pre-launch Checklist - -- [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. -- [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. -- [ ] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. -- [x] I signed the [CLA]. -- [ ] I listed at least one issue that this PR fixes in the description above. -- [ ] I updated/added relevant documentation (doc comments with `///`). -- [X] I added new tests to check the change I am making, or this PR is [test-exempt]. -- [ ] All existing and new tests are passing. - -If you need help, consider asking for advice on the #hackers-new channel on [Discord]. - - -[Contributor Guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview -[Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene -[test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests -[Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo -[Features we expect every widget to implement]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#features-we-expect-every-widget-to-implement -[CLA]: https://cla.developers.google.com/ -[flutter/tests]: https://github.com/flutter/tests -[breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes -[Discord]: https://github.com/flutter/flutter/wiki/Chat'''; - - final PullRequest pullRequest = generatePullRequest( - prNumber: 0, - repoName: slug.name, - title: prTitle, - // The test-only helper function `generatePullRequest` will interpolate - // this string into a JSON string which will then be decoded--thus, this string must be - // a valid JSON substring, with escaped newlines. - body: prBody.replaceAll('\n', r'\n'), - mergeable: true, - ); - githubService.pullRequestData = pullRequest; - githubService.mergeRequestMock = PullRequestMerge( - merged: true, - sha: pullRequest.mergeCommitSha, - ); - - final MergeResult result = await validationService.processMerge( - config: config, - messagePullRequest: pullRequest, - ); - - expect(result.result, isTrue); - expect(result.message, ''' -Various bugfixes and performance improvements. - -Fixes #12345 and #3. -This is the second line in a paragraph.'''); - }); - }); -} diff --git a/auto_submit/test/service/revert_issue_body_formatter_test.dart b/auto_submit/test/service/revert_issue_body_formatter_test.dart deleted file mode 100644 index 823914fb6..000000000 --- a/auto_submit/test/service/revert_issue_body_formatter_test.dart +++ /dev/null @@ -1,29 +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:auto_submit/service/revert_issue_body_formatter.dart'; -import 'package:github/github.dart'; -import 'package:test/test.dart'; - -void main() { - // Calls are made as they are done in git_cli_revert_method.dart. - test('Allow nullable fields in formatter.', () { - final PullRequest pullRequest = PullRequest(number: 123456, body: null, title: 'Interesting title.'); - const String sender = 'RevertAuthor'; - RevertIssueBodyFormatter? revertIssueBodyFormatter; - expect( - () => revertIssueBodyFormatter = RevertIssueBodyFormatter( - slug: RepositorySlug('flutter', 'flutter'), - originalPrNumber: pullRequest.number!, - initiatingAuthor: sender, - originalPrTitle: pullRequest.title, - originalPrBody: pullRequest.body, - ), - returnsNormally, - ); - revertIssueBodyFormatter!.format; - expect(revertIssueBodyFormatter, isNotNull); - expect(revertIssueBodyFormatter!.revertPrBody!.contains('No description provided.'), isTrue); - }); -} diff --git a/auto_submit/test/service/revert_request_validation_service_test.dart b/auto_submit/test/service/revert_request_validation_service_test.dart deleted file mode 100644 index d2dbc9bb0..000000000 --- a/auto_submit/test/service/revert_request_validation_service_test.dart +++ /dev/null @@ -1,1141 +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:async'; -import 'dart:convert'; - -import 'package:auto_submit/configuration/repository_configuration.dart'; -import 'package:auto_submit/model/auto_submit_query_result.dart' as auto hide PullRequest; -import 'package:auto_submit/requests/github_pull_request_event.dart'; -import 'package:auto_submit/service/revert_request_validation_service.dart'; -import 'package:auto_submit/service/validation_service.dart'; -import 'package:auto_submit/validations/validation.dart'; -import 'package:github/github.dart'; -import 'package:googleapis/bigquery/v2.dart'; -import 'package:mockito/mockito.dart'; -import 'package:retry/retry.dart'; -import 'package:test/test.dart'; - -import '../configuration/repository_configuration_data.dart'; -import '../requests/github_webhook_test_data.dart'; -import '../src/action/fake_revert_method.dart'; -import '../src/request_handling/fake_pubsub.dart'; -import '../src/service/fake_approver_service.dart'; -import '../src/service/fake_bigquery_service.dart'; -import '../src/service/fake_config.dart'; -import '../src/service/fake_graphql_client.dart'; -import '../src/service/fake_github_service.dart'; -import '../src/validations/fake_approval.dart'; -import '../src/validations/fake_mergeable.dart'; -import '../src/validations/fake_required_check_runs.dart'; -import '../src/validations/fake_validation_filter.dart'; -import '../utilities/utils.dart'; -import '../utilities/mocks.dart'; -import 'bigquery_test.dart'; - -void main() { - late RevertRequestValidationService validationService; - late FakeConfig config; - late FakeGithubService githubService; - late FakeGraphQLClient githubGraphQLClient; - late RepositorySlug slug; - - late MockJobsResource jobsResource; - late FakeBigqueryService bigqueryService; - late FakeRevertMethod revertMethod; - - setUp(() { - githubGraphQLClient = FakeGraphQLClient(); - githubService = FakeGithubService(client: MockGitHub()); - config = FakeConfig(githubService: githubService, githubGraphQLClient: githubGraphQLClient); - revertMethod = FakeRevertMethod(); - validationService = RevertRequestValidationService( - config, - retryOptions: const RetryOptions(delayFactor: Duration.zero, maxDelay: Duration.zero, maxAttempts: 1), - revertMethod: revertMethod, - ); - slug = RepositorySlug('flutter', 'cocoon'); - jobsResource = MockJobsResource(); - bigqueryService = FakeBigqueryService(jobsResource); - config.bigqueryService = bigqueryService; - config.repositoryConfigurationMock = RepositoryConfiguration.fromYaml(sampleConfigNoOverride); - - when(jobsResource.query(captureAny, any)).thenAnswer((Invocation invocation) { - return Future.value( - QueryResponse.fromJson(jsonDecode(insertDeleteUpdateSuccessResponse) as Map), - ); - }); - }); - - group('Testing time limit check:', () { - test('Pull request is rejected if merged over 24 hours ago.', () { - final PullRequest pullRequest = generatePullRequest( - prNumber: 0, - repoName: slug.name, - mergedAt: DateTime.now().subtract(const Duration(hours: 25)), - ); - expect(validationService.isWithinTimeLimit(pullRequest), isFalse); - }); - - test('Pull request is rejected if mergedAt is null', () { - final PullRequest pullRequest = PullRequest( - number: 0, - base: PullRequestHead(repo: Repository(name: slug.name)), - mergedAt: null, - ); - - expect(validationService.isWithinTimeLimit(pullRequest), isFalse); - }); - - test('Pull request is accepted if mergedAt is within 24 hours ago.', () { - final PullRequest pullRequest = generatePullRequest( - prNumber: 0, - repoName: slug.name, - mergedAt: DateTime.now().subtract(const Duration(hours: 23)), - ); - - expect(validationService.isWithinTimeLimit(pullRequest), isTrue); - }); - - test('Pull request is accepted if mergedAt is exactly 24 hours ago.', () { - final PullRequest pullRequest = generatePullRequest( - prNumber: 0, - repoName: slug.name, - mergedAt: DateTime.now().subtract(const Duration(hours: 24)), - ); - - expect(validationService.isWithinTimeLimit(pullRequest), isTrue); - }); - }); - - group('shouldProcess:', () { - test('Process revert from closed as "revert"', () async { - final PullRequest pullRequest = generatePullRequest(prNumber: 0, repoName: slug.name, state: 'closed'); - final IssueLabel issueLabel = IssueLabel(name: 'revert'); - final List labelNames = ['revert']; - pullRequest.labels = [issueLabel]; - githubService.pullRequestData = pullRequest; - final RevertProcessMethod revertProcessMethod = await validationService.shouldProcess(pullRequest, labelNames); - - expect(revertProcessMethod, RevertProcessMethod.revert); - }); - - test('Process open revert request as "revert of"', () async { - final PullRequest pullRequest = - generatePullRequest(prNumber: 0, repoName: slug.name, state: 'open', author: config.autosubmitBot); - final IssueLabel issueLabel = IssueLabel(name: 'revert of'); - final List labelNames = ['revert of']; - pullRequest.labels = [issueLabel]; - githubService.pullRequestData = pullRequest; - final RevertProcessMethod revertProcessMethod = await validationService.shouldProcess(pullRequest, labelNames); - - expect(revertProcessMethod, RevertProcessMethod.revertOf); - }); - - test('Pull request state is open with revert label is not processed', () async { - final PullRequest pullRequest = generatePullRequest(prNumber: 0, repoName: slug.name, state: 'open'); - final IssueLabel issueLabel = IssueLabel(name: 'revert'); - final List labelNames = ['revert']; - pullRequest.labels = [issueLabel]; - githubService.pullRequestData = pullRequest; - final RevertProcessMethod revertProcessMethod = await validationService.shouldProcess(pullRequest, labelNames); - - expect(revertProcessMethod, RevertProcessMethod.none); - }); - - test('Pull request is closed with "revert of" label is not processed', () async { - final PullRequest pullRequest = - generatePullRequest(prNumber: 0, repoName: slug.name, state: 'closed', author: config.autosubmitBot); - final IssueLabel issueLabel = IssueLabel(name: 'revert of'); - final List labelNames = ['revert of']; - pullRequest.labels = [issueLabel]; - githubService.pullRequestData = pullRequest; - final RevertProcessMethod revertProcessMethod = await validationService.shouldProcess(pullRequest, labelNames); - - expect(revertProcessMethod, RevertProcessMethod.none); - }); - - test('"revert of" pull request not authored by autosubmit bot is not processed.', () async { - final PullRequest pullRequest = - generatePullRequest(prNumber: 0, repoName: slug.name, state: 'open', author: 'octocat'); - final IssueLabel issueLabel = IssueLabel(name: 'revert of'); - final List labelNames = ['revert of']; - pullRequest.labels = [issueLabel]; - githubService.pullRequestData = pullRequest; - final RevertProcessMethod revertProcessMethod = await validationService.shouldProcess(pullRequest, labelNames); - - expect(revertProcessMethod, RevertProcessMethod.none); - }); - - test('Closed pull request not processed if it was not merged', () async { - final PullRequest pullRequest = PullRequest( - number: 0, - base: PullRequestHead(repo: Repository(name: slug.name)), - state: 'closed', - mergedAt: null, - ); - final IssueLabel issueLabel = IssueLabel(name: 'revert'); - final List labelNames = ['revert']; - pullRequest.labels = [issueLabel]; - githubService.pullRequestData = pullRequest; - final RevertProcessMethod revertProcessMethod = await validationService.shouldProcess(pullRequest, labelNames); - - expect(revertProcessMethod, RevertProcessMethod.none); - }); - }); - - group('Process revert pull requests:', () { - test('Remove label and post comment when issue has passed time limit to be reverted.', () async { - // setup objects - final FakePubSub pubsub = FakePubSub(); - - final PullRequestHelper flutterRequest = PullRequestHelper( - prNumber: 0, - lastCommitHash: oid, - reviews: [], - mergedAt: DateTime.now().subtract(const Duration(hours: 25)), - ); - - final PullRequest pullRequest = generatePullRequest( - prNumber: 0, - repoName: slug.name, - mergedAt: DateTime.now().subtract(const Duration(hours: 25)), - ); - - final auto.QueryResult queryResult = createQueryResult(flutterRequest); - - final GithubPullRequestEvent githubPullRequestEvent = GithubPullRequestEvent( - pullRequest: pullRequest, - action: 'labeled', - sender: User(login: 'ricardoamador'), - ); - - // setup fields - githubService.createCommentData = createCommentMock; - githubService.pullRequestMock = pullRequest; - - // run tests - unawaited(pubsub.publish(config.pubsubRevertRequestSubscription, pullRequest)); - await validationService.processRevertRequest( - result: queryResult, - githubPullRequestEvent: githubPullRequestEvent, - ackId: 'test', - pubsub: pubsub, - ); - - // validate - expect(githubService.issueComment, isNotNull); - expect(githubService.labelRemoved, true); - assert(pubsub.messagesQueue.isEmpty); - }); - - test('Create the new revert issue from the closed one.', () async { - // setup - final FakePubSub pubsub = FakePubSub(); - - final PullRequestHelper flutterRequest = PullRequestHelper( - prNumber: 0, - lastCommitHash: oid, - reviews: [], - ); - - final PullRequest pullRequest = generatePullRequest(prNumber: 0, repoName: slug.name, author: 'auto-submit[bot]'); - - final auto.QueryResult queryResult = createQueryResult(flutterRequest); - - final GithubPullRequestEvent githubPullRequestEvent = GithubPullRequestEvent( - pullRequest: pullRequest, - action: 'labeled', - sender: User(login: 'auto-submit[bot]'), - ); - - // setup fields - githubService.createCommentData = createCommentMock; - githubService.pullRequestMock = pullRequest; - revertMethod.object = pullRequest; - - // run test - unawaited(pubsub.publish(config.pubsubRevertRequestSubscription, pullRequest)); - await validationService.processRevertRequest( - result: queryResult, - githubPullRequestEvent: githubPullRequestEvent, - ackId: 'test', - pubsub: pubsub, - ); - - // validate - expect(githubService.issueComment, isNull); - expect(githubService.labelRemoved, true); - assert(pubsub.messagesQueue.isEmpty); - }); - - test('New revert request is not created, label is removed.', () async { - // setup - final FakePubSub pubsub = FakePubSub(); - - final PullRequestHelper flutterRequest = PullRequestHelper( - prNumber: 0, - lastCommitHash: oid, - reviews: [], - ); - - final PullRequest pullRequest = generatePullRequest( - prNumber: 0, - repoName: slug.name, - ); - - final auto.QueryResult queryResult = createQueryResult(flutterRequest); - - final GithubPullRequestEvent githubPullRequestEvent = GithubPullRequestEvent( - pullRequest: pullRequest, - action: 'labeled', - sender: User(login: 'ricardoamador'), - ); - - // setup fields - githubService.createCommentData = createCommentMock; - revertMethod.throwException = true; - revertMethod.object = queryResult.repository!.pullRequest; - githubService.pullRequestMock = pullRequest; - - // run test - unawaited(pubsub.publish(config.pubsubRevertRequestSubscription, pullRequest)); - await validationService.processRevertRequest( - result: queryResult, - githubPullRequestEvent: githubPullRequestEvent, - ackId: 'test', - pubsub: pubsub, - ); - - // validate - expect(githubService.issueComment, isNotNull); - expect(githubService.labelRemoved, true); - assert(pubsub.messagesQueue.isEmpty); - }); - }); - - group('Process "revert of" pull requests:', () { - test('Pull request is not processed due to repo config', () async { - // setup - config.repositoryConfigurationMock = RepositoryConfiguration.fromYaml(sampleConfigRevertReviewRequired); - final FakePubSub pubsub = FakePubSub(); - - final PullRequestHelper flutterRequest = PullRequestHelper( - prNumber: 0, - lastCommitHash: oid, - reviews: [], - ); - - final auto.QueryResult queryResult = createQueryResult(flutterRequest); - - final PullRequest pullRequest = generatePullRequest( - prNumber: 0, - repoName: slug.name, - labelName: 'revert of', - body: 'Reverts flutter/flutter#1234', - ); - - final GithubPullRequestEvent githubPullRequestEvent = GithubPullRequestEvent( - pullRequest: pullRequest, - action: 'labeled', - sender: User(login: 'ricardoamador'), - ); - - final Issue issue = Issue( - id: 1234, - assignee: User(login: 'keyonghan'), - createdAt: DateTime.now(), - ); - - // setup fields - githubService.githubIssueMock = issue; - githubService.pullRequestMock = pullRequest; - githubService.createCommentData = createCommentMock; - validationService.approverService = FakeApproverService(config); - - // run test - unawaited(pubsub.publish(config.pubsubRevertRequestSubscription, pullRequest)); - await validationService.processRevertOfRequest( - result: queryResult, - githubPullRequestEvent: githubPullRequestEvent, - ackId: 'test', - pubsub: pubsub, - ); - - // validate - expect(githubService.issueComment, isNotNull); - expect(githubService.labelRemoved, true); - assert(pubsub.messagesQueue.isEmpty); - }); - - test('Validation failure, label is removed.', () async { - // setup - final FakeValidationFilter fakeValidationFilter = FakeValidationFilter(); - final FakeApproval fakeApproval = FakeApproval(config: config); - fakeApproval.validationResult = - ValidationResult(true, Action.REMOVE_LABEL, 'This PR has met approval requirements for merging.\n'); - final FakeRequiredCheckRuns fakeRequiredCheckRuns = FakeRequiredCheckRuns(config: config); - fakeRequiredCheckRuns.validationResult = - ValidationResult(true, Action.REMOVE_LABEL, 'All required check runs have completed.'); - final FakeMergeable fakeMergeable = FakeMergeable(config: config); - fakeMergeable.validationResult = ValidationResult( - false, - Action.REMOVE_LABEL, - 'Pull request flutter/flutter/1234 is not in a mergeable state.', - ); - fakeValidationFilter.registerValidation(fakeApproval); - fakeValidationFilter.registerValidation(fakeRequiredCheckRuns); - fakeValidationFilter.registerValidation(fakeMergeable); - - final FakePubSub pubsub = FakePubSub(); - - final PullRequestHelper flutterRequest = PullRequestHelper( - prNumber: 0, - lastCommitHash: oid, - reviews: [], - ); - - final auto.QueryResult queryResult = createQueryResult(flutterRequest); - - final PullRequest pullRequest = generatePullRequest( - prNumber: 0, - repoName: slug.name, - labelName: 'revert of', - body: 'Reverts flutter/flutter#1234', - ); - - final GithubPullRequestEvent githubPullRequestEvent = GithubPullRequestEvent( - pullRequest: pullRequest, - action: 'labeled', - sender: User(login: 'ricardoamador'), - ); - - final Issue issue = Issue( - id: 1234, - assignee: User(login: 'keyonghan'), - createdAt: DateTime.now(), - ); - - // setup fields - githubService.githubIssueMock = issue; - githubService.pullRequestMock = pullRequest; - githubService.createCommentData = createCommentMock; - validationService.approverService = FakeApproverService(config); - validationService.validationFilter = fakeValidationFilter; - - // run test - unawaited(pubsub.publish(config.pubsubRevertRequestSubscription, pullRequest)); - await validationService.processRevertOfRequest( - result: queryResult, - githubPullRequestEvent: githubPullRequestEvent, - ackId: 'test', - pubsub: pubsub, - ); - - // validate - expect(githubService.issueComment, isNotNull); - expect(githubService.labelRemoved, true); - assert(pubsub.messagesQueue.isEmpty); - }); - - test('Temporary validation failure label not removed.', () async { - // setup - final FakeValidationFilter fakeValidationFilter = FakeValidationFilter(); - final FakeApproval fakeApproval = FakeApproval(config: config); - fakeApproval.validationResult = - ValidationResult(true, Action.REMOVE_LABEL, 'This PR has met approval requirements for merging.\n'); - final FakeRequiredCheckRuns fakeRequiredCheckRuns = FakeRequiredCheckRuns(config: config); - fakeRequiredCheckRuns.validationResult = - ValidationResult(false, Action.IGNORE_TEMPORARILY, 'All required check runs have not yet completed.'); - final FakeMergeable fakeMergeable = FakeMergeable(config: config); - fakeMergeable.validationResult = - ValidationResult(true, Action.REMOVE_LABEL, 'Pull request flutter/flutter/1234 is in a mergeable state.'); - fakeValidationFilter.registerValidation(fakeApproval); - fakeValidationFilter.registerValidation(fakeRequiredCheckRuns); - fakeValidationFilter.registerValidation(fakeMergeable); - - final FakePubSub pubsub = FakePubSub(); - - final PullRequestHelper flutterRequest = PullRequestHelper( - prNumber: 0, - lastCommitHash: oid, - reviews: [], - ); - - final auto.QueryResult queryResult = createQueryResult(flutterRequest); - - final PullRequest pullRequest = generatePullRequest( - prNumber: 0, - repoName: slug.name, - labelName: 'revert of', - body: 'Reverts flutter/flutter#1234', - ); - - final GithubPullRequestEvent githubPullRequestEvent = GithubPullRequestEvent( - pullRequest: pullRequest, - action: 'labeled', - sender: User(login: 'ricardoamador'), - ); - - final Issue issue = Issue( - id: 1234, - assignee: User(login: 'keyonghan'), - createdAt: DateTime.now(), - ); - - // setup fields - githubService.githubIssueMock = issue; - githubService.pullRequestMock = pullRequest; - githubService.createCommentData = createCommentMock; - validationService.approverService = FakeApproverService(config); - validationService.validationFilter = fakeValidationFilter; - - // run test - unawaited(pubsub.publish(config.pubsubRevertRequestSubscription, pullRequest)); - await validationService.processRevertOfRequest( - result: queryResult, - githubPullRequestEvent: githubPullRequestEvent, - ackId: 'test', - pubsub: pubsub, - ); - - // validate - expect(githubService.issueComment, isNull); - expect(githubService.labelRemoved, false); - assert(pubsub.messagesQueue.isNotEmpty); - }); - - test('Temp and hard failure result in label removed', () async { - // setup - final FakeValidationFilter fakeValidationFilter = FakeValidationFilter(); - final FakeApproval fakeApproval = FakeApproval(config: config); - fakeApproval.validationResult = - ValidationResult(true, Action.REMOVE_LABEL, 'This PR has met approval requirements for merging.\n'); - final FakeRequiredCheckRuns fakeRequiredCheckRuns = FakeRequiredCheckRuns(config: config); - fakeRequiredCheckRuns.validationResult = - ValidationResult(false, Action.IGNORE_TEMPORARILY, 'All required check runs have not yet completed.'); - final FakeMergeable fakeMergeable = FakeMergeable(config: config); - fakeMergeable.validationResult = ValidationResult( - false, - Action.REMOVE_LABEL, - 'Pull request flutter/flutter/1234 is not in a mergeable state.', - ); - fakeValidationFilter.registerValidation(fakeApproval); - fakeValidationFilter.registerValidation(fakeRequiredCheckRuns); - fakeValidationFilter.registerValidation(fakeMergeable); - - final FakePubSub pubsub = FakePubSub(); - - final PullRequestHelper flutterRequest = PullRequestHelper( - prNumber: 0, - lastCommitHash: oid, - reviews: [], - ); - - final auto.QueryResult queryResult = createQueryResult(flutterRequest); - - final PullRequest pullRequest = generatePullRequest( - prNumber: 0, - repoName: slug.name, - labelName: 'revert of', - body: 'Reverts flutter/flutter#1234', - ); - - final GithubPullRequestEvent githubPullRequestEvent = GithubPullRequestEvent( - pullRequest: pullRequest, - action: 'labeled', - sender: User(login: 'ricardoamador'), - ); - - final Issue issue = Issue( - id: 1234, - assignee: User(login: 'keyonghan'), - createdAt: DateTime.now(), - ); - - // setup fields - githubService.githubIssueMock = issue; - githubService.pullRequestMock = pullRequest; - githubService.createCommentData = createCommentMock; - validationService.approverService = FakeApproverService(config); - validationService.validationFilter = fakeValidationFilter; - - // run test - unawaited(pubsub.publish(config.pubsubRevertRequestSubscription, pullRequest)); - await validationService.processRevertOfRequest( - result: queryResult, - githubPullRequestEvent: githubPullRequestEvent, - ackId: 'test', - pubsub: pubsub, - ); - - // validate - expect(githubService.issueComment, isNotNull); - expect(githubService.labelRemoved, true); - assert(pubsub.messagesQueue.isEmpty); - }); - - test('Merge valid "revert of" request', () async { - // setup - final FakeValidationFilter fakeValidationFilter = FakeValidationFilter(); - final FakeApproval fakeApproval = FakeApproval(config: config); - fakeApproval.validationResult = ValidationResult( - true, - Action.REMOVE_LABEL, - 'This PR has met approval requirements for merging.\n', - ); - final FakeRequiredCheckRuns fakeRequiredCheckRuns = FakeRequiredCheckRuns(config: config); - fakeRequiredCheckRuns.validationResult = ValidationResult( - true, - Action.REMOVE_LABEL, - 'All required check runs have completed.', - ); - final FakeMergeable fakeMergeable = FakeMergeable(config: config); - fakeMergeable.validationResult = ValidationResult( - true, - Action.REMOVE_LABEL, - 'Pull request flutter/flutter/1234 is in a mergeable state.', - ); - fakeValidationFilter.registerValidation(fakeApproval); - fakeValidationFilter.registerValidation(fakeRequiredCheckRuns); - fakeValidationFilter.registerValidation(fakeMergeable); - - final FakePubSub pubsub = FakePubSub(); - - final PullRequestHelper flutterRequest = PullRequestHelper( - prNumber: 0, - lastCommitHash: oid, - reviews: [], - ); - - final auto.QueryResult queryResult = createQueryResult(flutterRequest); - - final PullRequest pullRequest = generatePullRequest( - prNumber: 0, - repoName: slug.name, - labelName: 'revert of', - body: 'Reverts flutter/flutter#1234', - ); - - final GithubPullRequestEvent githubPullRequestEvent = GithubPullRequestEvent( - pullRequest: pullRequest, - action: 'labeled', - sender: User(login: 'auto-submit[bot]'), - ); - - final Issue issue = Issue( - id: 1234, - assignee: User(login: 'keyonghan'), - createdAt: DateTime.now(), - ); - - // setup fields - githubService.githubIssueMock = issue; - githubService.pullRequestMock = pullRequest; - githubService.createCommentData = createCommentMock; - githubService.mergeRequestMock = PullRequestMerge( - merged: true, - sha: 'sha', - message: 'Pull Request successfully merged', - ); - validationService.approverService = FakeApproverService(config); - validationService.validationFilter = fakeValidationFilter; - - // run test - unawaited(pubsub.publish(config.pubsubRevertRequestSubscription, pullRequest)); - await validationService.processRevertOfRequest( - result: queryResult, - githubPullRequestEvent: githubPullRequestEvent, - ackId: 'test', - pubsub: pubsub, - ); - - // validate - expect(githubService.issueComment, isNull); - expect(githubService.labelRemoved, false); - assert(pubsub.messagesQueue.isEmpty); - }); - - test('Unable to merge valid "revert of" request.', () async { - // setup - final FakeValidationFilter fakeValidationFilter = FakeValidationFilter(); - final FakeApproval fakeApproval = FakeApproval(config: config); - fakeApproval.validationResult = ValidationResult( - true, - Action.REMOVE_LABEL, - 'This PR has met approval requirements for merging.\n', - ); - final FakeRequiredCheckRuns fakeRequiredCheckRuns = FakeRequiredCheckRuns(config: config); - fakeRequiredCheckRuns.validationResult = ValidationResult( - true, - Action.REMOVE_LABEL, - 'All required check runs have completed.', - ); - final FakeMergeable fakeMergeable = FakeMergeable(config: config); - fakeMergeable.validationResult = ValidationResult( - true, - Action.REMOVE_LABEL, - 'Pull request flutter/flutter/1234 is in a mergeable state.', - ); - fakeValidationFilter.registerValidation(fakeApproval); - fakeValidationFilter.registerValidation(fakeRequiredCheckRuns); - fakeValidationFilter.registerValidation(fakeMergeable); - - final FakePubSub pubsub = FakePubSub(); - - final PullRequestHelper flutterRequest = PullRequestHelper( - prNumber: 0, - lastCommitHash: oid, - reviews: [], - ); - - final auto.QueryResult queryResult = createQueryResult(flutterRequest); - - final PullRequest pullRequest = generatePullRequest( - prNumber: 0, - repoName: slug.name, - labelName: 'revert of', - body: 'Reverts flutter/flutter#1234', - ); - - final GithubPullRequestEvent githubPullRequestEvent = GithubPullRequestEvent( - pullRequest: pullRequest, - action: 'labeled', - sender: User(login: 'ricardoamador'), - ); - - final Issue issue = Issue( - id: 1234, - assignee: User(login: 'keyonghan'), - createdAt: DateTime.now(), - ); - - // setup fields - githubService.githubIssueMock = issue; - githubService.pullRequestMock = pullRequest; - githubService.createCommentData = createCommentMock; - githubService.mergeRequestMock = PullRequestMerge( - merged: false, - sha: 'sha', - message: 'Pull Request was not merged successfully', - ); - validationService.approverService = FakeApproverService(config); - validationService.validationFilter = fakeValidationFilter; - - // run test - unawaited(pubsub.publish(config.pubsubRevertRequestSubscription, pullRequest)); - await validationService.processRevertOfRequest( - result: queryResult, - githubPullRequestEvent: githubPullRequestEvent, - ackId: 'test', - pubsub: pubsub, - ); - - // validate - expect(githubService.issueComment, isNotNull); - expect(githubService.labelRemoved, true); - assert(pubsub.messagesQueue.isEmpty); - }); - - test('Exhaust retries on merge on retryable error. Unable to merge.', () async { - // setup - validationService = RevertRequestValidationService( - config, - retryOptions: const RetryOptions( - delayFactor: Duration.zero, - maxDelay: Duration.zero, - // three attempts - maxAttempts: 3, - ), - ); - - final FakeValidationFilter fakeValidationFilter = FakeValidationFilter(); - final FakeApproval fakeApproval = FakeApproval(config: config); - fakeApproval.validationResult = ValidationResult( - true, - Action.REMOVE_LABEL, - 'This PR has met approval requirements for merging.\n', - ); - final FakeRequiredCheckRuns fakeRequiredCheckRuns = FakeRequiredCheckRuns(config: config); - fakeRequiredCheckRuns.validationResult = ValidationResult( - true, - Action.REMOVE_LABEL, - 'All required check runs have completed.', - ); - final FakeMergeable fakeMergeable = FakeMergeable(config: config); - fakeMergeable.validationResult = ValidationResult( - true, - Action.REMOVE_LABEL, - 'Pull request flutter/flutter/1234 is in a mergeable state.', - ); - fakeValidationFilter.registerValidation(fakeApproval); - fakeValidationFilter.registerValidation(fakeRequiredCheckRuns); - fakeValidationFilter.registerValidation(fakeMergeable); - - final FakePubSub pubsub = FakePubSub(); - - final PullRequestHelper flutterRequest = PullRequestHelper( - prNumber: 0, - lastCommitHash: oid, - reviews: [], - ); - - final auto.QueryResult queryResult = createQueryResult(flutterRequest); - - final PullRequest pullRequest = generatePullRequest( - prNumber: 0, - repoName: slug.name, - labelName: 'revert of', - body: 'Reverts flutter/flutter#1234', - ); - - final GithubPullRequestEvent githubPullRequestEvent = GithubPullRequestEvent( - pullRequest: pullRequest, - action: 'labeled', - sender: User(login: 'ricardoamador'), - ); - - final Issue issue = Issue( - id: 1234, - assignee: User(login: 'keyonghan'), - createdAt: DateTime.now(), - ); - - // setup fields - githubService.githubIssueMock = issue; - githubService.pullRequestMock = pullRequest; - githubService.createCommentData = createCommentMock; - - githubService.mergeRequestMock = PullRequestMerge( - merged: false, - sha: 'sha', - message: 'Pull Request was not merged successfully', - ); - - validationService.approverService = FakeApproverService(config); - validationService.validationFilter = fakeValidationFilter; - - // run test - unawaited(pubsub.publish(config.pubsubRevertRequestSubscription, pullRequest)); - await validationService.processRevertOfRequest( - result: queryResult, - githubPullRequestEvent: githubPullRequestEvent, - ackId: 'test', - pubsub: pubsub, - ); - - // validate - expect(githubService.issueComment, isNotNull); - expect(githubService.labelRemoved, true); - assert(pubsub.messagesQueue.isEmpty); - }); - - test('Merge fails first time then succeeds after retry.', () async { - // setup - validationService = RevertRequestValidationService( - config, - retryOptions: const RetryOptions( - delayFactor: Duration.zero, - maxDelay: Duration.zero, - // three attempts - maxAttempts: 3, - ), - ); - - final FakeValidationFilter fakeValidationFilter = FakeValidationFilter(); - final FakeApproval fakeApproval = FakeApproval(config: config); - fakeApproval.validationResult = ValidationResult( - true, - Action.REMOVE_LABEL, - 'This PR has met approval requirements for merging.\n', - ); - final FakeRequiredCheckRuns fakeRequiredCheckRuns = FakeRequiredCheckRuns(config: config); - fakeRequiredCheckRuns.validationResult = ValidationResult( - true, - Action.REMOVE_LABEL, - 'All required check runs have completed.', - ); - final FakeMergeable fakeMergeable = FakeMergeable(config: config); - fakeMergeable.validationResult = ValidationResult( - true, - Action.REMOVE_LABEL, - 'Pull request flutter/flutter/1234 is in a mergeable state.', - ); - fakeValidationFilter.registerValidation(fakeApproval); - fakeValidationFilter.registerValidation(fakeRequiredCheckRuns); - fakeValidationFilter.registerValidation(fakeMergeable); - - final FakePubSub pubsub = FakePubSub(); - - final PullRequestHelper flutterRequest = PullRequestHelper( - prNumber: 0, - lastCommitHash: oid, - reviews: [], - ); - - final auto.QueryResult queryResult = createQueryResult(flutterRequest); - - final PullRequest pullRequest = generatePullRequest( - prNumber: 0, - repoName: slug.name, - labelName: 'revert of', - body: 'Reverts flutter/flutter#1234', - ); - - final GithubPullRequestEvent githubPullRequestEvent = GithubPullRequestEvent( - pullRequest: pullRequest, - action: 'labeled', - sender: User(login: 'ricardoamador'), - ); - - final Issue issue = Issue( - id: 1234, - assignee: User(login: 'keyonghan'), - createdAt: DateTime.now(), - ); - - // setup fields - githubService.githubIssueMock = issue; - githubService.pullRequestMock = pullRequest; - githubService.createCommentData = createCommentMock; - - // TODO use the mock list. - githubService.useMergeRequestMockList = true; - final PullRequestMerge pullRequestMergeFail = PullRequestMerge( - merged: false, - sha: 'sha', - message: 'Pull request was not merged successfully', - ); - final PullRequestMerge pullRequestMergeSuccess = PullRequestMerge( - merged: true, - sha: 'sha', - message: 'Pull request was merged successfully', - ); - - githubService.pullRequestMergeMockList = [pullRequestMergeFail, pullRequestMergeSuccess]; - - validationService.approverService = FakeApproverService(config); - validationService.validationFilter = fakeValidationFilter; - - // run test - unawaited(pubsub.publish(config.pubsubRevertRequestSubscription, pullRequest)); - await validationService.processRevertOfRequest( - result: queryResult, - githubPullRequestEvent: githubPullRequestEvent, - ackId: 'test', - pubsub: pubsub, - ); - - // validate - expect(githubService.issueComment, isNull); - expect(githubService.labelRemoved, false); - assert(pubsub.messagesQueue.isEmpty); - }); - - test('Merge is not retried on non retryable exception', () async { - // setup - validationService = RevertRequestValidationService( - config, - retryOptions: const RetryOptions( - delayFactor: Duration.zero, - maxDelay: Duration.zero, - // three attempts - maxAttempts: 3, - ), - ); - - final FakeValidationFilter fakeValidationFilter = FakeValidationFilter(); - final FakeApproval fakeApproval = FakeApproval(config: config); - fakeApproval.validationResult = ValidationResult( - true, - Action.REMOVE_LABEL, - 'This PR has met approval requirements for merging.\n', - ); - final FakeRequiredCheckRuns fakeRequiredCheckRuns = FakeRequiredCheckRuns(config: config); - fakeRequiredCheckRuns.validationResult = ValidationResult( - true, - Action.REMOVE_LABEL, - 'All required check runs have completed.', - ); - final FakeMergeable fakeMergeable = FakeMergeable(config: config); - fakeMergeable.validationResult = ValidationResult( - true, - Action.REMOVE_LABEL, - 'Pull request flutter/flutter/1234 is in a mergeable state.', - ); - fakeValidationFilter.registerValidation(fakeApproval); - fakeValidationFilter.registerValidation(fakeRequiredCheckRuns); - fakeValidationFilter.registerValidation(fakeMergeable); - - final FakePubSub pubsub = FakePubSub(); - - final PullRequestHelper flutterRequest = PullRequestHelper( - prNumber: 0, - lastCommitHash: oid, - reviews: [], - ); - - final auto.QueryResult queryResult = createQueryResult(flutterRequest); - - final PullRequest pullRequest = generatePullRequest( - prNumber: 0, - repoName: slug.name, - labelName: 'revert of', - body: 'Reverts flutter/flutter#1234', - ); - - final GithubPullRequestEvent githubPullRequestEvent = GithubPullRequestEvent( - pullRequest: pullRequest, - action: 'labeled', - sender: User(login: 'ricardoamador'), - ); - - final Issue issue = Issue( - id: 1234, - assignee: User(login: 'keyonghan'), - createdAt: DateTime.now(), - ); - - // setup fields - githubService.githubIssueMock = issue; - githubService.pullRequestMock = pullRequest; - githubService.createCommentData = createCommentMock; - - githubService.throwExceptionOnMerge = true; - githubService.mergeRequestMock = PullRequestMerge( - merged: false, - sha: 'sha', - message: 'Pull Request was not merged successfully', - ); - - validationService.approverService = FakeApproverService(config); - validationService.validationFilter = fakeValidationFilter; - - // run test - unawaited(pubsub.publish(config.pubsubRevertRequestSubscription, pullRequest)); - await validationService.processRevertOfRequest( - result: queryResult, - githubPullRequestEvent: githubPullRequestEvent, - ackId: 'test', - pubsub: pubsub, - ); - - // validate - expect(githubService.issueComment, isNotNull); - expect(githubService.labelRemoved, true); - assert(pubsub.messagesQueue.isEmpty); - }); - }); - - group('processMerge:', () { - test('Correct PR titles when merging to use Reland', () async { - final PullRequest pullRequest = generatePullRequest( - prNumber: 0, - repoName: slug.name, - title: 'Revert "Revert "My first PR!"', - mergeable: true, - ); - githubService.pullRequestData = pullRequest; - githubService.mergeRequestMock = PullRequestMerge( - merged: true, - sha: pullRequest.mergeCommitSha, - ); - - final MergeResult result = await validationService.processMerge( - config: config, - messagePullRequest: pullRequest, - ); - - expect(result.message, contains('Reland "My first PR!"')); - }); - - test('includes PR description in commit message', () async { - final PullRequest pullRequest = generatePullRequest( - prNumber: 0, - repoName: slug.name, - title: 'PR title', - // The test-only helper function `generatePullRequest` will interpolate - // this string into a JSON string which will then be decoded--thus, this string must be - // a valid JSON substring, with escaped newlines. - body: r'PR description\nwhich\nis multiline.', - mergeable: true, - ); - githubService.pullRequestData = pullRequest; - githubService.mergeRequestMock = PullRequestMerge( - merged: true, - sha: pullRequest.mergeCommitSha, - ); - final MergeResult result = await validationService.processMerge( - config: config, - messagePullRequest: pullRequest, - ); - - expect(result.message, ''' -PR description -which -is multiline.'''); - }); - - test('commit message filters out markdown checkboxes', () async { - const String prTitle = 'Important update #4'; - const String prBody = ''' -Various bugfixes and performance improvements. - -Fixes #12345 and #3. -This is the second line in a paragraph. - -## Pre-launch Checklist - -- [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. -- [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. -- [ ] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. -- [x] I signed the [CLA]. -- [ ] I listed at least one issue that this PR fixes in the description above. -- [ ] I updated/added relevant documentation (doc comments with `///`). -- [X] I added new tests to check the change I am making, or this PR is [test-exempt]. -- [ ] All existing and new tests are passing. - -If you need help, consider asking for advice on the #hackers-new channel on [Discord]. - - -[Contributor Guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview -[Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene -[test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests -[Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo -[Features we expect every widget to implement]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#features-we-expect-every-widget-to-implement -[CLA]: https://cla.developers.google.com/ -[flutter/tests]: https://github.com/flutter/tests -[breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes -[Discord]: https://github.com/flutter/flutter/wiki/Chat'''; - - final PullRequest pullRequest = generatePullRequest( - prNumber: 0, - repoName: slug.name, - title: prTitle, - // The test-only helper function `generatePullRequest` will interpolate - // this string into a JSON string which will then be decoded--thus, this string must be - // a valid JSON substring, with escaped newlines. - body: prBody.replaceAll('\n', r'\n'), - mergeable: true, - ); - githubService.pullRequestData = pullRequest; - githubService.mergeRequestMock = PullRequestMerge( - merged: true, - sha: pullRequest.mergeCommitSha, - ); - - final MergeResult result = await validationService.processMerge( - config: config, - messagePullRequest: pullRequest, - ); - - expect(result.result, isTrue); - expect(result.message, ''' -Various bugfixes and performance improvements. - -Fixes #12345 and #3. -This is the second line in a paragraph.'''); - }); - }); -} diff --git a/auto_submit/test/src/action/fake_revert_method.dart b/auto_submit/test/src/action/fake_revert_method.dart deleted file mode 100644 index 0e7eea042..000000000 --- a/auto_submit/test/src/action/fake_revert_method.dart +++ /dev/null @@ -1,20 +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:auto_submit/action/revert_method.dart'; -import 'package:auto_submit/service/config.dart'; -import 'package:github/src/common/model/pulls.dart'; - -class FakeRevertMethod implements RevertMethod { - Object? object; - bool throwException = false; - - @override - Future createRevert(Config config, String initiatingAuthor, PullRequest pullRequest) async { - if (throwException) { - throw 'Crappy github exception not related to the actual error.'; - } - return object; - } -} diff --git a/auto_submit/test/src/configuration/fake_repository_configuration_manager.dart b/auto_submit/test/src/configuration/fake_repository_configuration_manager.dart deleted file mode 100644 index b7fef72c2..000000000 --- a/auto_submit/test/src/configuration/fake_repository_configuration_manager.dart +++ /dev/null @@ -1,38 +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:auto_submit/configuration/repository_configuration.dart'; -import 'package:auto_submit/configuration/repository_configuration_manager.dart'; -import 'package:auto_submit/service/config.dart'; -import 'package:github/src/common/model/repos.dart'; -import 'package:neat_cache/neat_cache.dart'; - -class FakeRepositoryConfigurationManager implements RepositoryConfigurationManager { - FakeRepositoryConfigurationManager(this.config, this.cache); - - String? yamlConfig; - - @override - final Cache cache; - - @override - final Config config; - - late RepositoryConfiguration? repositoryConfigurationMock; - - late RepositoryConfiguration? mergedRepositoryConfigurationMock; - - @override - Future readRepositoryConfiguration(RepositorySlug slug) async { - return repositoryConfigurationMock!; - } - - @override - RepositoryConfiguration mergeConfigurations( - RepositoryConfiguration globalConfiguration, - RepositoryConfiguration localConfiguration, - ) { - return mergedRepositoryConfigurationMock!; - } -} diff --git a/auto_submit/test/src/request_handling/fake_authentication.dart b/auto_submit/test/src/request_handling/fake_authentication.dart deleted file mode 100644 index ab2159bd3..000000000 --- a/auto_submit/test/src/request_handling/fake_authentication.dart +++ /dev/null @@ -1,25 +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:auto_submit/request_handling/authentication.dart'; -import 'package:auto_submit/requests/exceptions.dart'; -import 'package:shelf/shelf.dart'; - -// ignore: must_be_immutable -class FakeCronAuthProvider implements CronAuthProvider { - FakeCronAuthProvider({ - this.authenticated = true, - }); - - bool authenticated; - - @override - Future authenticate(Request request) async { - if (authenticated) { - return true; - } else { - throw const Unauthenticated('Not authenticated'); - } - } -} diff --git a/auto_submit/test/src/request_handling/fake_pubsub.dart b/auto_submit/test/src/request_handling/fake_pubsub.dart deleted file mode 100644 index 75d5a4c95..000000000 --- a/auto_submit/test/src/request_handling/fake_pubsub.dart +++ /dev/null @@ -1,57 +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:math'; - -import 'package:auto_submit/request_handling/pubsub.dart'; -import 'package:googleapis/pubsub/v1.dart'; - -class FakePubSub extends PubSub { - List messagesQueue = []; - // The iteration of `pull` API calls. - int iteration = -1; - // Number of messages in each Pub/Sub pull call. This mocks the API - // returning random number of messages each time. - int messageSize = 2; - - @override - Future publish(String topicName, dynamic json) async { - final String messageData = jsonEncode(json); - final List messageBytes = utf8.encode(messageData); - final String messageBase64 = base64Encode(messageBytes); - messagesQueue.add(messageBase64); - } - - @override - Future pull(String subscription, int maxMessages) async { - // The list will be empty if there are no more messages available in the backlog. - final List receivedMessages = []; - iteration++; - if (messagesQueue.isNotEmpty) { - int i = iteration * messageSize; - // Returns only allowed max number of messages. The number should not be greater than - // `maxMessages`, the available messages, and the number allowed in each call. The - // last number is to mock real `pull` API call. - while (i < min(min(maxMessages, messagesQueue.length), (iteration + 1) * messageSize)) { - receivedMessages.add( - ReceivedMessage( - message: PubsubMessage(data: messagesQueue[i] as String, messageId: '$i'), - ackId: 'ackId_$i', - ), - ); - i++; - } - return PullResponse(receivedMessages: receivedMessages); - } - return PullResponse(receivedMessages: receivedMessages); - } - - @override - Future acknowledge(String subscription, String ackId) async { - if (messagesQueue.isNotEmpty) { - messagesQueue.removeAt(messagesQueue.length - 1); - } - } -} diff --git a/auto_submit/test/src/service/fake_approver_service.dart b/auto_submit/test/src/service/fake_approver_service.dart deleted file mode 100644 index 9acd9183f..000000000 --- a/auto_submit/test/src/service/fake_approver_service.dart +++ /dev/null @@ -1,15 +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. - -import 'package:auto_submit/service/approver_service.dart'; -import 'package:github/github.dart' as gh; - -class FakeApproverService extends ApproverService { - FakeApproverService(super.config); - - @override - Future autoApproval(gh.PullRequest pullRequest) async { - // no op - } -} diff --git a/auto_submit/test/src/service/fake_bigquery_service.dart b/auto_submit/test/src/service/fake_bigquery_service.dart deleted file mode 100644 index d9cbd3ad7..000000000 --- a/auto_submit/test/src/service/fake_bigquery_service.dart +++ /dev/null @@ -1,19 +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. - -import 'package:auto_submit/service/bigquery.dart'; -import 'package:googleapis/bigquery/v2.dart'; - -import '../../utilities/mocks.mocks.dart'; - -class FakeBigqueryService extends BigqueryService { - FakeBigqueryService(this.jobsResource) : super(MockAccessClientProvider()); - - JobsResource jobsResource; - - @override - Future defaultJobs() async { - return jobsResource; - } -} diff --git a/auto_submit/test/src/service/fake_config.dart b/auto_submit/test/src/service/fake_config.dart deleted file mode 100644 index d0454a9dd..000000000 --- a/auto_submit/test/src/service/fake_config.dart +++ /dev/null @@ -1,96 +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. - -import 'dart:async'; - -import 'package:auto_submit/configuration/repository_configuration.dart'; -import 'package:auto_submit/service/bigquery.dart'; -import 'package:auto_submit/service/config.dart'; -import 'package:auto_submit/service/github_service.dart'; -import 'package:auto_submit/service/secrets.dart'; -import 'package:github/github.dart'; -import 'package:neat_cache/neat_cache.dart'; -import 'package:graphql/client.dart'; - -import 'fake_github_service.dart'; - -// Represents a fake config to be used in unit test. -class FakeConfig extends Config { - FakeConfig({ - this.githubClient, - this.githubGraphQLClient, - this.githubService, - this.rollerAccountsValue, - this.overrideTreeStatusLabelValue, - this.webhookKey, - this.kPubsubPullNumberValue, - this.bigqueryService, - }) : super( - cacheProvider: Cache.inMemoryCacheProvider(4), - secretManager: LocalSecretManager(), - ); - - GitHub? githubClient; - GraphQLClient? githubGraphQLClient; - GithubService? githubService = FakeGithubService(); - Set? rollerAccountsValue; - String? overrideTreeStatusLabelValue; - String? webhookKey; - int? kPubsubPullNumberValue; - BigqueryService? bigqueryService; - RepositoryConfiguration? repositoryConfigurationMock; - - @override - String get pubsubPullRequestTopic => 'auto-submit-queue'; - @override - String get pubsubPullRequestSubscription => 'auto-submit-queue-sub'; - @override - String get pubsubRevertRequestTopic => 'auto-submit-revert-queue'; - @override - String get pubsubRevertRequestSubscription => 'auto-submit-revert-queue-sub'; - - @override - int get kPubsubPullNumber => kPubsubPullNumberValue ?? 1; - - @override - Future createGithubClient(RepositorySlug slug) async => githubClient!; - - @override - Future createFlutterGitHubBotClient(RepositorySlug slug) async => githubClient!; - - @override - Future createGithubService(RepositorySlug slug) async => githubService ?? FakeGithubService(); - - @override - Future createGitHubGraphQLClient(RepositorySlug slug) async => githubGraphQLClient!; - - @override - Set get rollerAccounts => - rollerAccountsValue ?? - const { - 'skia-flutter-autoroll', - 'engine-flutter-autoroll', - 'dependabot[bot]', - 'dependabot', - }; - - @override - String get overrideTreeStatusLabel => overrideTreeStatusLabelValue ?? 'warning: land on red to fix tree breakage'; - - @override - Future getWebhookKey() async { - return webhookKey ?? 'not_a_real_key'; - } - - @override - Future getFlutterGitHubBotToken() async { - return 'not_a_real_token'; - } - - @override - Future createBigQueryService() async => bigqueryService!; - - @override - Future getRepositoryConfiguration(RepositorySlug slug) async => repositoryConfigurationMock!; -} diff --git a/auto_submit/test/src/service/fake_github_service.dart b/auto_submit/test/src/service/fake_github_service.dart deleted file mode 100644 index 00b0f0d7c..000000000 --- a/auto_submit/test/src/service/fake_github_service.dart +++ /dev/null @@ -1,390 +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. - -import 'dart:convert'; - -import 'package:auto_submit/service/github_service.dart'; -import 'package:github/github.dart'; -import 'package:shelf/src/response.dart'; - -import '../../utilities/mocks.dart'; - -/// A fake GithubService implementation. -class FakeGithubService implements GithubService { - FakeGithubService({ - MockGitHub? client, - String? checkRunsMock, - String? commitMock, - String? pullRequest, - String? compareTwoCommitsMock, - String? successMergeMock, - String? createCommentMock, - String? pullRequestMergeMock, - }) : github = client ?? MockGitHub(); - - @override - final MockGitHub github; - - String? checkRunsMock; - String? commitMock; - PullRequest? pullRequestMock; - String? compareTwoCommitsMock; - String? successMergeMock; - String? createCommentMock; - String? pullRequestMergeMock; - String? pullRequestFilesJsonMock; - Issue? githubIssueMock; - String? githubFileContents; - - bool useMergeRequestMockList = false; - bool trackMergeRequestCalls = false; - PullRequestMerge? mergeRequestMock; - List pullRequestMergeMockList = []; - - /// map to track pull request calls using pull number and repository slug. - Map verifyPullRequestMergeCallMap = {}; - - bool throwOnCreateIssue = false; - - /// Setting either of these flags to true will pop the front element from the - /// list. Setting either to false will just return the non list version from - /// the appropriate method. - bool usePullRequestList = false; - bool usePullRequestFilesList = false; - - List pullRequestFilesMockList = []; - List pullRequestMockList = []; - - IssueComment? issueComment; - bool useRealComment = false; - bool labelRemoved = false; - - bool compareReturnValue = false; - bool skipRealCompare = false; - - set checkRunsData(String? checkRunsMock) { - this.checkRunsMock = checkRunsMock; - } - - set commitData(String? commitMock) { - this.commitMock = commitMock; - } - - set pullRequestData(PullRequest? pullRequestMock) { - this.pullRequestMock = pullRequestMock; - } - - set compareTwoCommitsData(String? compareTwoCommitsMock) { - this.compareTwoCommitsMock = compareTwoCommitsMock; - } - - set successMergeData(String? successMergeMock) { - this.successMergeMock = successMergeMock; - } - - set createCommentData(String? createCommentMock) { - this.createCommentMock = createCommentMock; - } - - set pullRequestMergeData(String? pullRequestMergeMock) { - this.pullRequestMergeMock = pullRequestMergeMock; - } - - set pullrequestFilesData(String? pullRequestFilesMock) { - pullRequestFilesJsonMock = pullRequestFilesMock; - } - - set githubIssue(Issue? issue) { - githubIssueMock = issue; - } - - @override - Future> getCheckRuns( - RepositorySlug slug, - String ref, - ) async { - final rawBody = json.decode(checkRunsMock!) as Map; - final List checkRunsBody = rawBody['check_runs']! as List; - final List checkRuns = []; - if ((checkRunsBody[0] as Map).isNotEmpty) { - checkRuns.addAll( - checkRunsBody.map((dynamic checkRun) => CheckRun.fromJson(checkRun as Map)).toList(), - ); - } - return checkRuns; - } - - @override - Future> getCheckRunsFiltered({ - required RepositorySlug slug, - required String ref, - String? checkName, - CheckRunStatus? status, - CheckRunFilter? filter, - }) async { - final List checkRuns = await getCheckRuns(slug, ref); - if (checkName != null) { - final List checkRunsFilteredByName = []; - for (CheckRun checkRun in checkRuns) { - if (checkRun.name == checkName) { - checkRunsFilteredByName.add(checkRun); - } - } - return checkRunsFilteredByName; - } - return checkRuns; - } - - @override - Future getCommit(RepositorySlug slug, String sha) async { - final RepositoryCommit commit = RepositoryCommit.fromJson(jsonDecode(commitMock!) as Map); - return commit; - } - - @override - Future getPullRequest(RepositorySlug slug, int pullRequestNumber) async { - PullRequest pullRequest; - if (usePullRequestList && pullRequestMockList.isNotEmpty) { - pullRequest = pullRequestMockList.removeAt(0)!; - } else if (usePullRequestList && pullRequestMockList.isEmpty) { - throw Exception('List is empty.'); - } else { - pullRequest = pullRequestMock!; - } - return pullRequest; - } - - @override - Future compareTwoCommits(RepositorySlug slug, String refBase, String refHead) async { - final GitHubComparison githubComparison = - GitHubComparison.fromJson(jsonDecode(compareTwoCommitsMock!) as Map); - return githubComparison; - } - - @override - Future removeLabel(RepositorySlug slug, int issueNumber, String label) async { - labelRemoved = true; - return labelRemoved; - } - - @override - Future> addLabels(RepositorySlug slug, int issueNumber, List labels) async { - final List labelsAdded = []; - for (String labelName in labels) { - labelsAdded.add(IssueLabel(name: labelName)); - } - return labelsAdded; - } - - @override - Future createComment(RepositorySlug slug, int number, String commentBody) async { - if (useRealComment) { - issueComment = IssueComment(id: number, body: commentBody); - } else { - issueComment = IssueComment.fromJson(jsonDecode(createCommentMock!) as Map); - } - return issueComment!; - } - - @override - Future updateBranch(RepositorySlug slug, int number, String headSha) async { - return true; - } - - @override - Future autoMergeBranch(PullRequest pullRequest) { - // TODO: implement autoMergeBranch - throw UnimplementedError(); - } - - @override - Future> getPullRequestFiles(RepositorySlug slug, PullRequest pullRequest) async { - String pullRequestData; - - if (usePullRequestFilesList && pullRequestFilesMockList.isNotEmpty) { - pullRequestData = pullRequestFilesMockList.removeAt(0)!; - } else if (usePullRequestFilesList && pullRequestFilesMockList.isEmpty) { - throw Exception('File list is empty.'); - } else { - pullRequestData = pullRequestFilesJsonMock as String; - } - - final List pullRequestFileList = []; - - final dynamic parsedList = jsonDecode(pullRequestData); - - for (dynamic d in parsedList) { - final PullRequestFile file = PullRequestFile.fromJson(d as Map); - pullRequestFileList.add(file); - } - - return pullRequestFileList; - } - - @override - Future createIssue({ - required RepositorySlug slug, - required String title, - required String body, - List? labels, - String? assignee, - List? assignees, - String? state, - }) async { - if (throwOnCreateIssue) { - throw GitHubError(github, 'Exception on github create issue.'); - } - return githubIssueMock!; - } - - @override - Future getIssue({required RepositorySlug slug, required int issueNumber}) async { - return githubIssueMock!; - } - - bool throwExceptionOnMerge = false; - - /// If useMergeRequestMockList is true then we will return elements from that - /// list until it is empty. - /// - /// The developer should track the number of times this method is called as - /// managing an empty list is not done here. - @override - Future mergePullRequest( - RepositorySlug slug, - int number, { - String? commitMessage, - MergeMethod? mergeMethod, - String? requestSha, - }) async { - if (throwExceptionOnMerge) { - throw Exception('Exception occurred during merging of pull request.'); - } - verifyPullRequestMergeCallMap[number] = slug; - if (useMergeRequestMockList) { - return pullRequestMergeMockList.removeAt(0); - } else { - return mergeRequestMock!; - } - } - - void verifyMergePullRequests(Map expected) { - assert(verifyPullRequestMergeCallMap.length == expected.length); - verifyPullRequestMergeCallMap.forEach((key, value) { - assert(expected.containsKey(key)); - assert(expected[key] == value); - }); - } - - bool throwExceptionFileContents = false; - - List fileContentsMockList = []; - - @override - Future getFileContents(RepositorySlug slug, String path, {String? ref}) async { - if (throwExceptionFileContents) { - throw 'Contents do not point to a file.'; - } - - // Assume that the list is not empty. - return fileContentsMockList.removeAt(0); - } - - TeamMembershipState? teamMembershipStateMock = TeamMembershipState('active'); - - String defaultBranch = 'main'; - bool throwOnDefaultBranch = false; - Exception exception = Exception('Generic exception.'); - - @override - Future getDefaultBranch(RepositorySlug slug) async { - if (throwOnDefaultBranch) { - throw exception; - } else { - return defaultBranch; - } - } - - Repository repositoryMock = Repository(); - - @override - Future getRepository(RepositorySlug slug) async { - return repositoryMock; - } - - Map isTeamMemberMockMap = {}; - - @override - Future isTeamMember(String team, String user, String org) async { - if (!isTeamMemberMockMap.containsKey(user)) { - return false; - } - return isTeamMemberMockMap[user]!; - } - - @override - Future createPullRequest({ - required RepositorySlug slug, - String? title, - String? head, - required String base, - bool draft = false, - String? body, - }) { - // TODO: implement createPullRequest - throw UnimplementedError(); - } - - @override - Future> listPullRequests( - RepositorySlug slug, { - int? pages, - String? base, - String direction = 'desc', - String? head, - String sort = 'created', - String state = 'open', - }) { - // TODO: implement listPullRequests - throw UnimplementedError(); - } - - String? branchMockData; - - set branchMock(String data) => branchMock = data; - - @override - Future getBranch(RepositorySlug slug, String branchName) async { - return Branch.fromJson(json.decode(branchMockData!)); - } - - bool addReviewersToPullRequestMock = true; - - @override - Future addReviewersToPullRequest( - RepositorySlug slug, - int pullRequestNumber, - List reviewerLogins, - ) async { - return addReviewersToPullRequestMock; - } - - bool addAssigneeMock = true; - - @override - Future addAssignee( - RepositorySlug slug, - int number, - List assignees, - ) async { - return addAssigneeMock; - } - - bool deleteBranchMock = true; - - @override - Future deleteBranch(RepositorySlug slug, String branchName) async { - return deleteBranchMock; - } -} diff --git a/auto_submit/test/src/service/fake_graphql_client.dart b/auto_submit/test/src/service/fake_graphql_client.dart deleted file mode 100644 index 7dac634f6..000000000 --- a/auto_submit/test/src/service/fake_graphql_client.dart +++ /dev/null @@ -1,134 +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:gql/ast.dart'; -import 'package:graphql/client.dart'; -import 'package:test/test.dart'; - -class FakeGraphQLClient implements GraphQLClient { - late QueryResult Function(MutationOptions) mutateResultForOptions; - late QueryResult Function(QueryOptions) queryResultForOptions; - - @override - late QueryManager queryManager; - - @override - Link get link => throw UnimplementedError(); - - final List queries = []; - final List mutations = []; - - // This allows us to simulate returning QueryResults in an order. - final List mutationMap = []; - bool useMutationMapOnMutate = false; - - @override - Future> mutate(MutationOptions options) async { - mutations.add(options); - if (useMutationMapOnMutate) { - return mutationMap.removeAt(0) as QueryResult; - } - return mutateResultForOptions(options) as QueryResult; - } - - @override - Future> query(QueryOptions options) async { - queries.add(options); - return queryResultForOptions(options) as QueryResult; - } - - void verifyQueries(List expected) { - expect(queries.length, expected.length); - for (int i = 0; i < queries.length; i++) { - expect( - queries[i].properties, - equals(expected[i].properties), - ); - } - } - - void verifyMutations(List expected) { - expect(mutations.length, expected.length); - for (int i = 0; i < mutations.length; i++) { - expect( - mutations[i].properties, - equals(expected[i].properties), - ); - } - } - - @override - late DefaultPolicies defaultPolicies; - - @override - Map readFragment(FragmentRequest fragmentRequest, {bool? optimistic = true}) { - throw UnimplementedError(); - } - - @override - Map readQuery(Request request, {bool? optimistic = true}) { - throw UnimplementedError(); - } - - @override - Future> resetStore({bool refetchQueries = true}) { - throw UnimplementedError(); - } - - @override - void writeFragment(FragmentRequest fragmentRequest, {bool? broadcast = true, Map? data}) {} - - @override - void writeQuery(Request request, {Map? data, bool? broadcast = true}) {} - - @override - GraphQLCache get cache => throw UnimplementedError(); - - @override - GraphQLClient copyWith({ - Link? link, - GraphQLCache? cache, - DefaultPolicies? defaultPolicies, - bool? alwaysRebroadcast, - }) { - throw UnimplementedError(); - } - - @override - Future> fetchMore( - FetchMoreOptions fetchMoreOptions, { - required QueryOptions originalOptions, - required QueryResult previousResult, - }) { - throw UnimplementedError(); - } - - @override - Stream> subscribe(SubscriptionOptions options) { - throw UnimplementedError(); - } - - @override - ObservableQuery watchMutation(WatchQueryOptions options) { - throw UnimplementedError(); - } - - @override - ObservableQuery watchQuery(WatchQueryOptions options) { - throw UnimplementedError(); - } -} - -QueryResult createFakeQueryResult({ - Map? data, - OperationException? exception, -}) => - QueryResult( - data: data, - exception: exception, - options: QueryOptions( - document: const DocumentNode(), - ), - source: QueryResultSource.network, - ); diff --git a/auto_submit/test/src/validations/fake_approval.dart b/auto_submit/test/src/validations/fake_approval.dart deleted file mode 100644 index 370bdda72..000000000 --- a/auto_submit/test/src/validations/fake_approval.dart +++ /dev/null @@ -1,24 +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:auto_submit/model/auto_submit_query_result.dart'; - -import 'package:auto_submit/validations/approval.dart'; -import 'package:auto_submit/validations/validation.dart'; -import 'package:github/github.dart' as github; - -class FakeApproval extends Approval { - FakeApproval({required super.config}); - - ValidationResult? validationResult; - - @override - String get name => 'FakeApproval'; - - /// Implements the code review approval logic. - @override - Future validate(QueryResult result, github.PullRequest messagePullRequest) async { - return validationResult ?? ValidationResult(true, Action.REMOVE_LABEL, ''); - } -} diff --git a/auto_submit/test/src/validations/fake_mergeable.dart b/auto_submit/test/src/validations/fake_mergeable.dart deleted file mode 100644 index 47fd8c43d..000000000 --- a/auto_submit/test/src/validations/fake_mergeable.dart +++ /dev/null @@ -1,22 +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:auto_submit/model/auto_submit_query_result.dart'; -import 'package:auto_submit/validations/mergeable.dart'; -import 'package:auto_submit/validations/validation.dart'; -import 'package:github/github.dart' as github; - -class FakeMergeable extends Mergeable { - FakeMergeable({required super.config}); - - ValidationResult? validationResult; - - @override - String get name => 'FakeMergeable'; - - @override - Future validate(QueryResult result, github.PullRequest messagePullRequest) async { - return validationResult ?? ValidationResult(true, Action.REMOVE_LABEL, ''); - } -} diff --git a/auto_submit/test/src/validations/fake_required_check_runs.dart b/auto_submit/test/src/validations/fake_required_check_runs.dart deleted file mode 100644 index 9fbca5846..000000000 --- a/auto_submit/test/src/validations/fake_required_check_runs.dart +++ /dev/null @@ -1,23 +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:auto_submit/validations/required_check_runs.dart'; -import 'package:auto_submit/model/auto_submit_query_result.dart' as auto; - -import 'package:auto_submit/validations/validation.dart'; -import 'package:github/github.dart' as github; - -class FakeRequiredCheckRuns extends RequiredCheckRuns { - FakeRequiredCheckRuns({required super.config}); - - ValidationResult? validationResult; - - @override - String get name => 'FakeRequiredCheckRuns'; - - @override - Future validate(auto.QueryResult result, github.PullRequest messagePullRequest) async { - return validationResult ?? ValidationResult(true, Action.REMOVE_LABEL, ''); - } -} diff --git a/auto_submit/test/src/validations/fake_validation_filter.dart b/auto_submit/test/src/validations/fake_validation_filter.dart deleted file mode 100644 index 9dcc2034e..000000000 --- a/auto_submit/test/src/validations/fake_validation_filter.dart +++ /dev/null @@ -1,19 +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:auto_submit/validations/validation.dart'; -import 'package:auto_submit/validations/validation_filter.dart'; - -class FakeValidationFilter implements ValidationFilter { - final Set validations = {}; - - void registerValidation(Validation newValidation) { - validations.add(newValidation); - } - - @override - Set getValidations() { - return validations; - } -} diff --git a/auto_submit/test/utilities/mocks.dart b/auto_submit/test/utilities/mocks.dart deleted file mode 100644 index 06dafe078..000000000 --- a/auto_submit/test/utilities/mocks.dart +++ /dev/null @@ -1,24 +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. - -import 'package:auto_submit/service/access_client_provider.dart'; -import 'package:auto_submit/service/approver_service.dart'; -import 'package:github/github.dart'; -import 'package:googleapis/bigquery/v2.dart'; -import 'package:mockito/annotations.dart'; -import 'package:http/http.dart' as http; - -export 'mocks.mocks.dart'; - -@GenerateMocks([ - AccessClientProvider, - JobsResource, - ApproverService, - GitHub, - PullRequestsService, - RepositoriesService, - GitHubComparison, - http.Response, -]) -void main() {} diff --git a/auto_submit/test/utilities/mocks.mocks.dart b/auto_submit/test/utilities/mocks.mocks.dart deleted file mode 100644 index cfa5a26cd..000000000 --- a/auto_submit/test/utilities/mocks.mocks.dart +++ /dev/null @@ -1,3169 +0,0 @@ -// Mocks generated by Mockito 5.4.2 from annotations -// in auto_submit/test/utilities/mocks.dart. -// Do not manually edit this file. - -// ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i6; -import 'dart:typed_data' as _i11; - -import 'package:_discoveryapis_commons/_discoveryapis_commons.dart' as _i8; -import 'package:auto_submit/service/access_client_provider.dart' as _i7; -import 'package:auto_submit/service/approver_service.dart' as _i9; -import 'package:auto_submit/service/config.dart' as _i4; -import 'package:github/github.dart' as _i5; -import 'package:googleapis/bigquery/v2.dart' as _i3; -import 'package:http/http.dart' as _i2; -import 'package:mockito/mockito.dart' as _i1; -import 'package:mockito/src/dummies.dart' as _i10; - -// ignore_for_file: type=lint -// ignore_for_file: avoid_redundant_argument_values -// ignore_for_file: avoid_setters_without_getters -// ignore_for_file: comment_references -// ignore_for_file: implementation_imports -// ignore_for_file: invalid_use_of_visible_for_testing_member -// ignore_for_file: prefer_const_constructors -// ignore_for_file: unnecessary_parenthesis -// ignore_for_file: camel_case_types -// ignore_for_file: subtype_of_sealed_class - -class _FakeClient_0 extends _i1.SmartFake implements _i2.Client { - _FakeClient_0( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeJobCancelResponse_1 extends _i1.SmartFake implements _i3.JobCancelResponse { - _FakeJobCancelResponse_1( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeJob_2 extends _i1.SmartFake implements _i3.Job { - _FakeJob_2( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeGetQueryResultsResponse_3 extends _i1.SmartFake implements _i3.GetQueryResultsResponse { - _FakeGetQueryResultsResponse_3( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeJobList_4 extends _i1.SmartFake implements _i3.JobList { - _FakeJobList_4( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeQueryResponse_5 extends _i1.SmartFake implements _i3.QueryResponse { - _FakeQueryResponse_5( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeConfig_6 extends _i1.SmartFake implements _i4.Config { - _FakeConfig_6( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeAuthentication_7 extends _i1.SmartFake implements _i5.Authentication { - _FakeAuthentication_7( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeActivityService_8 extends _i1.SmartFake implements _i5.ActivityService { - _FakeActivityService_8( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeAuthorizationsService_9 extends _i1.SmartFake implements _i5.AuthorizationsService { - _FakeAuthorizationsService_9( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeGistsService_10 extends _i1.SmartFake implements _i5.GistsService { - _FakeGistsService_10( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeGitService_11 extends _i1.SmartFake implements _i5.GitService { - _FakeGitService_11( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeIssuesService_12 extends _i1.SmartFake implements _i5.IssuesService { - _FakeIssuesService_12( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeMiscService_13 extends _i1.SmartFake implements _i5.MiscService { - _FakeMiscService_13( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeOrganizationsService_14 extends _i1.SmartFake implements _i5.OrganizationsService { - _FakeOrganizationsService_14( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakePullRequestsService_15 extends _i1.SmartFake implements _i5.PullRequestsService { - _FakePullRequestsService_15( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeRepositoriesService_16 extends _i1.SmartFake implements _i5.RepositoriesService { - _FakeRepositoriesService_16( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeSearchService_17 extends _i1.SmartFake implements _i5.SearchService { - _FakeSearchService_17( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeUrlShortenerService_18 extends _i1.SmartFake implements _i5.UrlShortenerService { - _FakeUrlShortenerService_18( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeUsersService_19 extends _i1.SmartFake implements _i5.UsersService { - _FakeUsersService_19( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeChecksService_20 extends _i1.SmartFake implements _i5.ChecksService { - _FakeChecksService_20( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeFuture_21 extends _i1.SmartFake implements _i6.Future { - _FakeFuture_21( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeResponse_22 extends _i1.SmartFake implements _i2.Response { - _FakeResponse_22( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeGitHub_23 extends _i1.SmartFake implements _i5.GitHub { - _FakeGitHub_23( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakePullRequest_24 extends _i1.SmartFake implements _i5.PullRequest { - _FakePullRequest_24( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakePullRequestMerge_25 extends _i1.SmartFake implements _i5.PullRequestMerge { - _FakePullRequestMerge_25( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakePullRequestComment_26 extends _i1.SmartFake implements _i5.PullRequestComment { - _FakePullRequestComment_26( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakePullRequestReview_27 extends _i1.SmartFake implements _i5.PullRequestReview { - _FakePullRequestReview_27( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeRepository_28 extends _i1.SmartFake implements _i5.Repository { - _FakeRepository_28( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeLicenseDetails_29 extends _i1.SmartFake implements _i5.LicenseDetails { - _FakeLicenseDetails_29( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeLanguageBreakdown_30 extends _i1.SmartFake implements _i5.LanguageBreakdown { - _FakeLanguageBreakdown_30( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeBranch_31 extends _i1.SmartFake implements _i5.Branch { - _FakeBranch_31( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeCommitComment_32 extends _i1.SmartFake implements _i5.CommitComment { - _FakeCommitComment_32( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeRepositoryCommit_33 extends _i1.SmartFake implements _i5.RepositoryCommit { - _FakeRepositoryCommit_33( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeGitHubComparison_34 extends _i1.SmartFake implements _i5.GitHubComparison { - _FakeGitHubComparison_34( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeGitHubFile_35 extends _i1.SmartFake implements _i5.GitHubFile { - _FakeGitHubFile_35( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeRepositoryContents_36 extends _i1.SmartFake implements _i5.RepositoryContents { - _FakeRepositoryContents_36( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeContentCreation_37 extends _i1.SmartFake implements _i5.ContentCreation { - _FakeContentCreation_37( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeHook_38 extends _i1.SmartFake implements _i5.Hook { - _FakeHook_38( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakePublicKey_39 extends _i1.SmartFake implements _i5.PublicKey { - _FakePublicKey_39( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeRepositoryPages_40 extends _i1.SmartFake implements _i5.RepositoryPages { - _FakeRepositoryPages_40( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakePageBuild_41 extends _i1.SmartFake implements _i5.PageBuild { - _FakePageBuild_41( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeRelease_42 extends _i1.SmartFake implements _i5.Release { - _FakeRelease_42( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeReleaseAsset_43 extends _i1.SmartFake implements _i5.ReleaseAsset { - _FakeReleaseAsset_43( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeContributorParticipation_44 extends _i1.SmartFake implements _i5.ContributorParticipation { - _FakeContributorParticipation_44( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeRepositoryStatus_45 extends _i1.SmartFake implements _i5.RepositoryStatus { - _FakeRepositoryStatus_45( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeCombinedRepositoryStatus_46 extends _i1.SmartFake implements _i5.CombinedRepositoryStatus { - _FakeCombinedRepositoryStatus_46( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeReleaseNotes_47 extends _i1.SmartFake implements _i5.ReleaseNotes { - _FakeReleaseNotes_47( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -/// A class which mocks [AccessClientProvider]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockAccessClientProvider extends _i1.Mock implements _i7.AccessClientProvider { - MockAccessClientProvider() { - _i1.throwOnMissingStub(this); - } - - @override - _i6.Future<_i2.Client> createAccessClient( - {List? scopes = const [r'https://www.googleapis.com/auth/cloud-platform']}) => - (super.noSuchMethod( - Invocation.method( - #createAccessClient, - [], - {#scopes: scopes}, - ), - returnValue: _i6.Future<_i2.Client>.value(_FakeClient_0( - this, - Invocation.method( - #createAccessClient, - [], - {#scopes: scopes}, - ), - )), - ) as _i6.Future<_i2.Client>); -} - -/// A class which mocks [JobsResource]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockJobsResource extends _i1.Mock implements _i3.JobsResource { - MockJobsResource() { - _i1.throwOnMissingStub(this); - } - - @override - _i6.Future<_i3.JobCancelResponse> cancel( - String? projectId, - String? jobId, { - String? location, - String? $fields, - }) => - (super.noSuchMethod( - Invocation.method( - #cancel, - [ - projectId, - jobId, - ], - { - #location: location, - #$fields: $fields, - }, - ), - returnValue: _i6.Future<_i3.JobCancelResponse>.value(_FakeJobCancelResponse_1( - this, - Invocation.method( - #cancel, - [ - projectId, - jobId, - ], - { - #location: location, - #$fields: $fields, - }, - ), - )), - ) as _i6.Future<_i3.JobCancelResponse>); - @override - _i6.Future delete( - String? projectId, - String? jobId, { - String? location, - String? $fields, - }) => - (super.noSuchMethod( - Invocation.method( - #delete, - [ - projectId, - jobId, - ], - { - #location: location, - #$fields: $fields, - }, - ), - returnValue: _i6.Future.value(), - returnValueForMissingStub: _i6.Future.value(), - ) as _i6.Future); - @override - _i6.Future<_i3.Job> get( - String? projectId, - String? jobId, { - String? location, - String? $fields, - }) => - (super.noSuchMethod( - Invocation.method( - #get, - [ - projectId, - jobId, - ], - { - #location: location, - #$fields: $fields, - }, - ), - returnValue: _i6.Future<_i3.Job>.value(_FakeJob_2( - this, - Invocation.method( - #get, - [ - projectId, - jobId, - ], - { - #location: location, - #$fields: $fields, - }, - ), - )), - ) as _i6.Future<_i3.Job>); - @override - _i6.Future<_i3.GetQueryResultsResponse> getQueryResults( - String? projectId, - String? jobId, { - String? location, - int? maxResults, - String? pageToken, - String? startIndex, - int? timeoutMs, - String? $fields, - }) => - (super.noSuchMethod( - Invocation.method( - #getQueryResults, - [ - projectId, - jobId, - ], - { - #location: location, - #maxResults: maxResults, - #pageToken: pageToken, - #startIndex: startIndex, - #timeoutMs: timeoutMs, - #$fields: $fields, - }, - ), - returnValue: _i6.Future<_i3.GetQueryResultsResponse>.value(_FakeGetQueryResultsResponse_3( - this, - Invocation.method( - #getQueryResults, - [ - projectId, - jobId, - ], - { - #location: location, - #maxResults: maxResults, - #pageToken: pageToken, - #startIndex: startIndex, - #timeoutMs: timeoutMs, - #$fields: $fields, - }, - ), - )), - ) as _i6.Future<_i3.GetQueryResultsResponse>); - @override - _i6.Future<_i3.Job> insert( - _i3.Job? request, - String? projectId, { - String? $fields, - _i8.UploadOptions? uploadOptions = _i8.UploadOptions.defaultOptions, - _i8.Media? uploadMedia, - }) => - (super.noSuchMethod( - Invocation.method( - #insert, - [ - request, - projectId, - ], - { - #$fields: $fields, - #uploadOptions: uploadOptions, - #uploadMedia: uploadMedia, - }, - ), - returnValue: _i6.Future<_i3.Job>.value(_FakeJob_2( - this, - Invocation.method( - #insert, - [ - request, - projectId, - ], - { - #$fields: $fields, - #uploadOptions: uploadOptions, - #uploadMedia: uploadMedia, - }, - ), - )), - ) as _i6.Future<_i3.Job>); - @override - _i6.Future<_i3.JobList> list( - String? projectId, { - bool? allUsers, - String? maxCreationTime, - int? maxResults, - String? minCreationTime, - String? pageToken, - String? parentJobId, - String? projection, - List? stateFilter, - String? $fields, - }) => - (super.noSuchMethod( - Invocation.method( - #list, - [projectId], - { - #allUsers: allUsers, - #maxCreationTime: maxCreationTime, - #maxResults: maxResults, - #minCreationTime: minCreationTime, - #pageToken: pageToken, - #parentJobId: parentJobId, - #projection: projection, - #stateFilter: stateFilter, - #$fields: $fields, - }, - ), - returnValue: _i6.Future<_i3.JobList>.value(_FakeJobList_4( - this, - Invocation.method( - #list, - [projectId], - { - #allUsers: allUsers, - #maxCreationTime: maxCreationTime, - #maxResults: maxResults, - #minCreationTime: minCreationTime, - #pageToken: pageToken, - #parentJobId: parentJobId, - #projection: projection, - #stateFilter: stateFilter, - #$fields: $fields, - }, - ), - )), - ) as _i6.Future<_i3.JobList>); - @override - _i6.Future<_i3.QueryResponse> query( - _i3.QueryRequest? request, - String? projectId, { - String? $fields, - }) => - (super.noSuchMethod( - Invocation.method( - #query, - [ - request, - projectId, - ], - {#$fields: $fields}, - ), - returnValue: _i6.Future<_i3.QueryResponse>.value(_FakeQueryResponse_5( - this, - Invocation.method( - #query, - [ - request, - projectId, - ], - {#$fields: $fields}, - ), - )), - ) as _i6.Future<_i3.QueryResponse>); -} - -/// A class which mocks [ApproverService]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockApproverService extends _i1.Mock implements _i9.ApproverService { - MockApproverService() { - _i1.throwOnMissingStub(this); - } - - @override - _i4.Config get config => (super.noSuchMethod( - Invocation.getter(#config), - returnValue: _FakeConfig_6( - this, - Invocation.getter(#config), - ), - ) as _i4.Config); - @override - _i6.Future> getAutoApprovalAccounts(_i5.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #getAutoApprovalAccounts, - [slug], - ), - returnValue: _i6.Future>.value({}), - ) as _i6.Future>); - @override - _i6.Future autoApproval(_i5.PullRequest? pullRequest) => (super.noSuchMethod( - Invocation.method( - #autoApproval, - [pullRequest], - ), - returnValue: _i6.Future.value(), - returnValueForMissingStub: _i6.Future.value(), - ) as _i6.Future); -} - -/// A class which mocks [GitHub]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockGitHub extends _i1.Mock implements _i5.GitHub { - MockGitHub() { - _i1.throwOnMissingStub(this); - } - - @override - _i5.Authentication get auth => (super.noSuchMethod( - Invocation.getter(#auth), - returnValue: _FakeAuthentication_7( - this, - Invocation.getter(#auth), - ), - ) as _i5.Authentication); - @override - set auth(_i5.Authentication? _auth) => super.noSuchMethod( - Invocation.setter( - #auth, - _auth, - ), - returnValueForMissingStub: null, - ); - @override - String get endpoint => (super.noSuchMethod( - Invocation.getter(#endpoint), - returnValue: '', - ) as String); - @override - String get version => (super.noSuchMethod( - Invocation.getter(#version), - returnValue: '', - ) as String); - @override - _i2.Client get client => (super.noSuchMethod( - Invocation.getter(#client), - returnValue: _FakeClient_0( - this, - Invocation.getter(#client), - ), - ) as _i2.Client); - @override - _i5.ActivityService get activity => (super.noSuchMethod( - Invocation.getter(#activity), - returnValue: _FakeActivityService_8( - this, - Invocation.getter(#activity), - ), - ) as _i5.ActivityService); - @override - _i5.AuthorizationsService get authorizations => (super.noSuchMethod( - Invocation.getter(#authorizations), - returnValue: _FakeAuthorizationsService_9( - this, - Invocation.getter(#authorizations), - ), - ) as _i5.AuthorizationsService); - @override - _i5.GistsService get gists => (super.noSuchMethod( - Invocation.getter(#gists), - returnValue: _FakeGistsService_10( - this, - Invocation.getter(#gists), - ), - ) as _i5.GistsService); - @override - _i5.GitService get git => (super.noSuchMethod( - Invocation.getter(#git), - returnValue: _FakeGitService_11( - this, - Invocation.getter(#git), - ), - ) as _i5.GitService); - @override - _i5.IssuesService get issues => (super.noSuchMethod( - Invocation.getter(#issues), - returnValue: _FakeIssuesService_12( - this, - Invocation.getter(#issues), - ), - ) as _i5.IssuesService); - @override - _i5.MiscService get misc => (super.noSuchMethod( - Invocation.getter(#misc), - returnValue: _FakeMiscService_13( - this, - Invocation.getter(#misc), - ), - ) as _i5.MiscService); - @override - _i5.OrganizationsService get organizations => (super.noSuchMethod( - Invocation.getter(#organizations), - returnValue: _FakeOrganizationsService_14( - this, - Invocation.getter(#organizations), - ), - ) as _i5.OrganizationsService); - @override - _i5.PullRequestsService get pullRequests => (super.noSuchMethod( - Invocation.getter(#pullRequests), - returnValue: _FakePullRequestsService_15( - this, - Invocation.getter(#pullRequests), - ), - ) as _i5.PullRequestsService); - @override - _i5.RepositoriesService get repositories => (super.noSuchMethod( - Invocation.getter(#repositories), - returnValue: _FakeRepositoriesService_16( - this, - Invocation.getter(#repositories), - ), - ) as _i5.RepositoriesService); - @override - _i5.SearchService get search => (super.noSuchMethod( - Invocation.getter(#search), - returnValue: _FakeSearchService_17( - this, - Invocation.getter(#search), - ), - ) as _i5.SearchService); - @override - _i5.UrlShortenerService get urlShortener => (super.noSuchMethod( - Invocation.getter(#urlShortener), - returnValue: _FakeUrlShortenerService_18( - this, - Invocation.getter(#urlShortener), - ), - ) as _i5.UrlShortenerService); - @override - _i5.UsersService get users => (super.noSuchMethod( - Invocation.getter(#users), - returnValue: _FakeUsersService_19( - this, - Invocation.getter(#users), - ), - ) as _i5.UsersService); - @override - _i5.ChecksService get checks => (super.noSuchMethod( - Invocation.getter(#checks), - returnValue: _FakeChecksService_20( - this, - Invocation.getter(#checks), - ), - ) as _i5.ChecksService); - @override - _i6.Future getJSON( - String? path, { - int? statusCode, - void Function(_i2.Response)? fail, - Map? headers, - Map? params, - _i5.JSONConverter? convert, - String? preview, - }) => - (super.noSuchMethod( - Invocation.method( - #getJSON, - [path], - { - #statusCode: statusCode, - #fail: fail, - #headers: headers, - #params: params, - #convert: convert, - #preview: preview, - }, - ), - returnValue: _i10.ifNotNull( - _i10.dummyValueOrNull( - this, - Invocation.method( - #getJSON, - [path], - { - #statusCode: statusCode, - #fail: fail, - #headers: headers, - #params: params, - #convert: convert, - #preview: preview, - }, - ), - ), - (T v) => _i6.Future.value(v), - ) ?? - _FakeFuture_21( - this, - Invocation.method( - #getJSON, - [path], - { - #statusCode: statusCode, - #fail: fail, - #headers: headers, - #params: params, - #convert: convert, - #preview: preview, - }, - ), - ), - ) as _i6.Future); - @override - _i6.Future postJSON( - String? path, { - int? statusCode, - void Function(_i2.Response)? fail, - Map? headers, - Map? params, - _i5.JSONConverter? convert, - dynamic body, - String? preview, - }) => - (super.noSuchMethod( - Invocation.method( - #postJSON, - [path], - { - #statusCode: statusCode, - #fail: fail, - #headers: headers, - #params: params, - #convert: convert, - #body: body, - #preview: preview, - }, - ), - returnValue: _i10.ifNotNull( - _i10.dummyValueOrNull( - this, - Invocation.method( - #postJSON, - [path], - { - #statusCode: statusCode, - #fail: fail, - #headers: headers, - #params: params, - #convert: convert, - #body: body, - #preview: preview, - }, - ), - ), - (T v) => _i6.Future.value(v), - ) ?? - _FakeFuture_21( - this, - Invocation.method( - #postJSON, - [path], - { - #statusCode: statusCode, - #fail: fail, - #headers: headers, - #params: params, - #convert: convert, - #body: body, - #preview: preview, - }, - ), - ), - ) as _i6.Future); - @override - _i6.Future putJSON( - String? path, { - int? statusCode, - void Function(_i2.Response)? fail, - Map? headers, - Map? params, - _i5.JSONConverter? convert, - dynamic body, - String? preview, - }) => - (super.noSuchMethod( - Invocation.method( - #putJSON, - [path], - { - #statusCode: statusCode, - #fail: fail, - #headers: headers, - #params: params, - #convert: convert, - #body: body, - #preview: preview, - }, - ), - returnValue: _i10.ifNotNull( - _i10.dummyValueOrNull( - this, - Invocation.method( - #putJSON, - [path], - { - #statusCode: statusCode, - #fail: fail, - #headers: headers, - #params: params, - #convert: convert, - #body: body, - #preview: preview, - }, - ), - ), - (T v) => _i6.Future.value(v), - ) ?? - _FakeFuture_21( - this, - Invocation.method( - #putJSON, - [path], - { - #statusCode: statusCode, - #fail: fail, - #headers: headers, - #params: params, - #convert: convert, - #body: body, - #preview: preview, - }, - ), - ), - ) as _i6.Future); - @override - _i6.Future patchJSON( - String? path, { - int? statusCode, - void Function(_i2.Response)? fail, - Map? headers, - Map? params, - _i5.JSONConverter? convert, - dynamic body, - String? preview, - }) => - (super.noSuchMethod( - Invocation.method( - #patchJSON, - [path], - { - #statusCode: statusCode, - #fail: fail, - #headers: headers, - #params: params, - #convert: convert, - #body: body, - #preview: preview, - }, - ), - returnValue: _i10.ifNotNull( - _i10.dummyValueOrNull( - this, - Invocation.method( - #patchJSON, - [path], - { - #statusCode: statusCode, - #fail: fail, - #headers: headers, - #params: params, - #convert: convert, - #body: body, - #preview: preview, - }, - ), - ), - (T v) => _i6.Future.value(v), - ) ?? - _FakeFuture_21( - this, - Invocation.method( - #patchJSON, - [path], - { - #statusCode: statusCode, - #fail: fail, - #headers: headers, - #params: params, - #convert: convert, - #body: body, - #preview: preview, - }, - ), - ), - ) as _i6.Future); - @override - _i6.Future requestJson( - String? method, - String? path, { - int? statusCode, - void Function(_i2.Response)? fail, - Map? headers, - Map? params, - _i5.JSONConverter? convert, - dynamic body, - String? preview, - }) => - (super.noSuchMethod( - Invocation.method( - #requestJson, - [ - method, - path, - ], - { - #statusCode: statusCode, - #fail: fail, - #headers: headers, - #params: params, - #convert: convert, - #body: body, - #preview: preview, - }, - ), - returnValue: _i10.ifNotNull( - _i10.dummyValueOrNull( - this, - Invocation.method( - #requestJson, - [ - method, - path, - ], - { - #statusCode: statusCode, - #fail: fail, - #headers: headers, - #params: params, - #convert: convert, - #body: body, - #preview: preview, - }, - ), - ), - (T v) => _i6.Future.value(v), - ) ?? - _FakeFuture_21( - this, - Invocation.method( - #requestJson, - [ - method, - path, - ], - { - #statusCode: statusCode, - #fail: fail, - #headers: headers, - #params: params, - #convert: convert, - #body: body, - #preview: preview, - }, - ), - ), - ) as _i6.Future); - @override - _i6.Future<_i2.Response> request( - String? method, - String? path, { - Map? headers, - Map? params, - dynamic body, - int? statusCode, - void Function(_i2.Response)? fail, - String? preview, - }) => - (super.noSuchMethod( - Invocation.method( - #request, - [ - method, - path, - ], - { - #headers: headers, - #params: params, - #body: body, - #statusCode: statusCode, - #fail: fail, - #preview: preview, - }, - ), - returnValue: _i6.Future<_i2.Response>.value(_FakeResponse_22( - this, - Invocation.method( - #request, - [ - method, - path, - ], - { - #headers: headers, - #params: params, - #body: body, - #statusCode: statusCode, - #fail: fail, - #preview: preview, - }, - ), - )), - ) as _i6.Future<_i2.Response>); - @override - Never handleStatusCode(_i2.Response? response) => (super.noSuchMethod( - Invocation.method( - #handleStatusCode, - [response], - ), - returnValue: null, - ) as Never); - @override - void dispose() => super.noSuchMethod( - Invocation.method( - #dispose, - [], - ), - returnValueForMissingStub: null, - ); -} - -/// A class which mocks [PullRequestsService]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockPullRequestsService extends _i1.Mock implements _i5.PullRequestsService { - MockPullRequestsService() { - _i1.throwOnMissingStub(this); - } - - @override - _i5.GitHub get github => (super.noSuchMethod( - Invocation.getter(#github), - returnValue: _FakeGitHub_23( - this, - Invocation.getter(#github), - ), - ) as _i5.GitHub); - @override - _i6.Stream<_i5.PullRequest> list( - _i5.RepositorySlug? slug, { - int? pages, - String? base, - String? direction = r'desc', - String? head, - String? sort = r'created', - String? state = r'open', - }) => - (super.noSuchMethod( - Invocation.method( - #list, - [slug], - { - #pages: pages, - #base: base, - #direction: direction, - #head: head, - #sort: sort, - #state: state, - }, - ), - returnValue: _i6.Stream<_i5.PullRequest>.empty(), - ) as _i6.Stream<_i5.PullRequest>); - @override - _i6.Future<_i5.PullRequest> get( - _i5.RepositorySlug? slug, - int? number, - ) => - (super.noSuchMethod( - Invocation.method( - #get, - [ - slug, - number, - ], - ), - returnValue: _i6.Future<_i5.PullRequest>.value(_FakePullRequest_24( - this, - Invocation.method( - #get, - [ - slug, - number, - ], - ), - )), - ) as _i6.Future<_i5.PullRequest>); - @override - _i6.Future<_i5.PullRequest> create( - _i5.RepositorySlug? slug, - _i5.CreatePullRequest? request, - ) => - (super.noSuchMethod( - Invocation.method( - #create, - [ - slug, - request, - ], - ), - returnValue: _i6.Future<_i5.PullRequest>.value(_FakePullRequest_24( - this, - Invocation.method( - #create, - [ - slug, - request, - ], - ), - )), - ) as _i6.Future<_i5.PullRequest>); - @override - _i6.Future<_i5.PullRequest> edit( - _i5.RepositorySlug? slug, - int? number, { - String? title, - String? body, - String? state, - String? base, - }) => - (super.noSuchMethod( - Invocation.method( - #edit, - [ - slug, - number, - ], - { - #title: title, - #body: body, - #state: state, - #base: base, - }, - ), - returnValue: _i6.Future<_i5.PullRequest>.value(_FakePullRequest_24( - this, - Invocation.method( - #edit, - [ - slug, - number, - ], - { - #title: title, - #body: body, - #state: state, - #base: base, - }, - ), - )), - ) as _i6.Future<_i5.PullRequest>); - @override - _i6.Stream<_i5.RepositoryCommit> listCommits( - _i5.RepositorySlug? slug, - int? number, - ) => - (super.noSuchMethod( - Invocation.method( - #listCommits, - [ - slug, - number, - ], - ), - returnValue: _i6.Stream<_i5.RepositoryCommit>.empty(), - ) as _i6.Stream<_i5.RepositoryCommit>); - @override - _i6.Stream<_i5.PullRequestFile> listFiles( - _i5.RepositorySlug? slug, - int? number, - ) => - (super.noSuchMethod( - Invocation.method( - #listFiles, - [ - slug, - number, - ], - ), - returnValue: _i6.Stream<_i5.PullRequestFile>.empty(), - ) as _i6.Stream<_i5.PullRequestFile>); - @override - _i6.Stream<_i5.PullRequestReview> listReviews( - _i5.RepositorySlug? slug, - int? number, - ) => - (super.noSuchMethod( - Invocation.method( - #listReviews, - [ - slug, - number, - ], - ), - returnValue: _i6.Stream<_i5.PullRequestReview>.empty(), - ) as _i6.Stream<_i5.PullRequestReview>); - @override - _i6.Future isMerged( - _i5.RepositorySlug? slug, - int? number, - ) => - (super.noSuchMethod( - Invocation.method( - #isMerged, - [ - slug, - number, - ], - ), - returnValue: _i6.Future.value(false), - ) as _i6.Future); - @override - _i6.Future<_i5.PullRequestMerge> merge( - _i5.RepositorySlug? slug, - int? number, { - String? message, - _i5.MergeMethod? mergeMethod = _i5.MergeMethod.merge, - String? requestSha, - }) => - (super.noSuchMethod( - Invocation.method( - #merge, - [ - slug, - number, - ], - { - #message: message, - #mergeMethod: mergeMethod, - #requestSha: requestSha, - }, - ), - returnValue: _i6.Future<_i5.PullRequestMerge>.value(_FakePullRequestMerge_25( - this, - Invocation.method( - #merge, - [ - slug, - number, - ], - { - #message: message, - #mergeMethod: mergeMethod, - #requestSha: requestSha, - }, - ), - )), - ) as _i6.Future<_i5.PullRequestMerge>); - @override - _i6.Stream<_i5.PullRequestComment> listCommentsByPullRequest( - _i5.RepositorySlug? slug, - int? number, - ) => - (super.noSuchMethod( - Invocation.method( - #listCommentsByPullRequest, - [ - slug, - number, - ], - ), - returnValue: _i6.Stream<_i5.PullRequestComment>.empty(), - ) as _i6.Stream<_i5.PullRequestComment>); - @override - _i6.Stream<_i5.PullRequestComment> listComments(_i5.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listComments, - [slug], - ), - returnValue: _i6.Stream<_i5.PullRequestComment>.empty(), - ) as _i6.Stream<_i5.PullRequestComment>); - @override - _i6.Future<_i5.PullRequestComment> createComment( - _i5.RepositorySlug? slug, - int? number, - _i5.CreatePullRequestComment? comment, - ) => - (super.noSuchMethod( - Invocation.method( - #createComment, - [ - slug, - number, - comment, - ], - ), - returnValue: _i6.Future<_i5.PullRequestComment>.value(_FakePullRequestComment_26( - this, - Invocation.method( - #createComment, - [ - slug, - number, - comment, - ], - ), - )), - ) as _i6.Future<_i5.PullRequestComment>); - @override - _i6.Future<_i5.PullRequestReview> createReview( - _i5.RepositorySlug? slug, - _i5.CreatePullRequestReview? review, - ) => - (super.noSuchMethod( - Invocation.method( - #createReview, - [ - slug, - review, - ], - ), - returnValue: _i6.Future<_i5.PullRequestReview>.value(_FakePullRequestReview_27( - this, - Invocation.method( - #createReview, - [ - slug, - review, - ], - ), - )), - ) as _i6.Future<_i5.PullRequestReview>); -} - -/// A class which mocks [RepositoriesService]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockRepositoriesService extends _i1.Mock implements _i5.RepositoriesService { - MockRepositoriesService() { - _i1.throwOnMissingStub(this); - } - - @override - _i5.GitHub get github => (super.noSuchMethod( - Invocation.getter(#github), - returnValue: _FakeGitHub_23( - this, - Invocation.getter(#github), - ), - ) as _i5.GitHub); - @override - _i6.Stream<_i5.Repository> listRepositories({ - String? type = r'owner', - String? sort = r'full_name', - String? direction = r'asc', - }) => - (super.noSuchMethod( - Invocation.method( - #listRepositories, - [], - { - #type: type, - #sort: sort, - #direction: direction, - }, - ), - returnValue: _i6.Stream<_i5.Repository>.empty(), - ) as _i6.Stream<_i5.Repository>); - @override - _i6.Stream<_i5.Repository> listUserRepositories( - String? user, { - String? type = r'owner', - String? sort = r'full_name', - String? direction = r'asc', - }) => - (super.noSuchMethod( - Invocation.method( - #listUserRepositories, - [user], - { - #type: type, - #sort: sort, - #direction: direction, - }, - ), - returnValue: _i6.Stream<_i5.Repository>.empty(), - ) as _i6.Stream<_i5.Repository>); - @override - _i6.Stream<_i5.Repository> listOrganizationRepositories( - String? org, { - String? type = r'all', - }) => - (super.noSuchMethod( - Invocation.method( - #listOrganizationRepositories, - [org], - {#type: type}, - ), - returnValue: _i6.Stream<_i5.Repository>.empty(), - ) as _i6.Stream<_i5.Repository>); - @override - _i6.Stream<_i5.Repository> listPublicRepositories({ - int? limit = 50, - DateTime? since, - }) => - (super.noSuchMethod( - Invocation.method( - #listPublicRepositories, - [], - { - #limit: limit, - #since: since, - }, - ), - returnValue: _i6.Stream<_i5.Repository>.empty(), - ) as _i6.Stream<_i5.Repository>); - @override - _i6.Future<_i5.Repository> createRepository( - _i5.CreateRepository? repository, { - String? org, - }) => - (super.noSuchMethod( - Invocation.method( - #createRepository, - [repository], - {#org: org}, - ), - returnValue: _i6.Future<_i5.Repository>.value(_FakeRepository_28( - this, - Invocation.method( - #createRepository, - [repository], - {#org: org}, - ), - )), - ) as _i6.Future<_i5.Repository>); - @override - _i6.Future<_i5.LicenseDetails> getLicense(_i5.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #getLicense, - [slug], - ), - returnValue: _i6.Future<_i5.LicenseDetails>.value(_FakeLicenseDetails_29( - this, - Invocation.method( - #getLicense, - [slug], - ), - )), - ) as _i6.Future<_i5.LicenseDetails>); - @override - _i6.Future<_i5.Repository> getRepository(_i5.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #getRepository, - [slug], - ), - returnValue: _i6.Future<_i5.Repository>.value(_FakeRepository_28( - this, - Invocation.method( - #getRepository, - [slug], - ), - )), - ) as _i6.Future<_i5.Repository>); - @override - _i6.Stream<_i5.Repository> getRepositories(List<_i5.RepositorySlug>? slugs) => (super.noSuchMethod( - Invocation.method( - #getRepositories, - [slugs], - ), - returnValue: _i6.Stream<_i5.Repository>.empty(), - ) as _i6.Stream<_i5.Repository>); - @override - _i6.Future<_i5.Repository> editRepository( - _i5.RepositorySlug? slug, { - String? name, - String? description, - String? homepage, - bool? private, - bool? hasIssues, - bool? hasWiki, - bool? hasDownloads, - }) => - (super.noSuchMethod( - Invocation.method( - #editRepository, - [slug], - { - #name: name, - #description: description, - #homepage: homepage, - #private: private, - #hasIssues: hasIssues, - #hasWiki: hasWiki, - #hasDownloads: hasDownloads, - }, - ), - returnValue: _i6.Future<_i5.Repository>.value(_FakeRepository_28( - this, - Invocation.method( - #editRepository, - [slug], - { - #name: name, - #description: description, - #homepage: homepage, - #private: private, - #hasIssues: hasIssues, - #hasWiki: hasWiki, - #hasDownloads: hasDownloads, - }, - ), - )), - ) as _i6.Future<_i5.Repository>); - @override - _i6.Future deleteRepository(_i5.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #deleteRepository, - [slug], - ), - returnValue: _i6.Future.value(false), - ) as _i6.Future); - @override - _i6.Stream<_i5.Contributor> listContributors( - _i5.RepositorySlug? slug, { - bool? anon = false, - }) => - (super.noSuchMethod( - Invocation.method( - #listContributors, - [slug], - {#anon: anon}, - ), - returnValue: _i6.Stream<_i5.Contributor>.empty(), - ) as _i6.Stream<_i5.Contributor>); - @override - _i6.Stream<_i5.Team> listTeams(_i5.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listTeams, - [slug], - ), - returnValue: _i6.Stream<_i5.Team>.empty(), - ) as _i6.Stream<_i5.Team>); - @override - _i6.Future<_i5.LanguageBreakdown> listLanguages(_i5.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listLanguages, - [slug], - ), - returnValue: _i6.Future<_i5.LanguageBreakdown>.value(_FakeLanguageBreakdown_30( - this, - Invocation.method( - #listLanguages, - [slug], - ), - )), - ) as _i6.Future<_i5.LanguageBreakdown>); - @override - _i6.Stream<_i5.Tag> listTags( - _i5.RepositorySlug? slug, { - int? page = 1, - int? pages, - int? perPage = 30, - }) => - (super.noSuchMethod( - Invocation.method( - #listTags, - [slug], - { - #page: page, - #pages: pages, - #perPage: perPage, - }, - ), - returnValue: _i6.Stream<_i5.Tag>.empty(), - ) as _i6.Stream<_i5.Tag>); - @override - _i6.Stream<_i5.Branch> listBranches(_i5.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listBranches, - [slug], - ), - returnValue: _i6.Stream<_i5.Branch>.empty(), - ) as _i6.Stream<_i5.Branch>); - @override - _i6.Future<_i5.Branch> getBranch( - _i5.RepositorySlug? slug, - String? branch, - ) => - (super.noSuchMethod( - Invocation.method( - #getBranch, - [ - slug, - branch, - ], - ), - returnValue: _i6.Future<_i5.Branch>.value(_FakeBranch_31( - this, - Invocation.method( - #getBranch, - [ - slug, - branch, - ], - ), - )), - ) as _i6.Future<_i5.Branch>); - @override - _i6.Stream<_i5.Collaborator> listCollaborators(_i5.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listCollaborators, - [slug], - ), - returnValue: _i6.Stream<_i5.Collaborator>.empty(), - ) as _i6.Stream<_i5.Collaborator>); - @override - _i6.Future isCollaborator( - _i5.RepositorySlug? slug, - String? user, - ) => - (super.noSuchMethod( - Invocation.method( - #isCollaborator, - [ - slug, - user, - ], - ), - returnValue: _i6.Future.value(false), - ) as _i6.Future); - @override - _i6.Future addCollaborator( - _i5.RepositorySlug? slug, - String? user, - ) => - (super.noSuchMethod( - Invocation.method( - #addCollaborator, - [ - slug, - user, - ], - ), - returnValue: _i6.Future.value(false), - ) as _i6.Future); - @override - _i6.Future removeCollaborator( - _i5.RepositorySlug? slug, - String? user, - ) => - (super.noSuchMethod( - Invocation.method( - #removeCollaborator, - [ - slug, - user, - ], - ), - returnValue: _i6.Future.value(false), - ) as _i6.Future); - @override - _i6.Stream<_i5.CommitComment> listSingleCommitComments( - _i5.RepositorySlug? slug, - _i5.RepositoryCommit? commit, - ) => - (super.noSuchMethod( - Invocation.method( - #listSingleCommitComments, - [ - slug, - commit, - ], - ), - returnValue: _i6.Stream<_i5.CommitComment>.empty(), - ) as _i6.Stream<_i5.CommitComment>); - @override - _i6.Stream<_i5.CommitComment> listCommitComments(_i5.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listCommitComments, - [slug], - ), - returnValue: _i6.Stream<_i5.CommitComment>.empty(), - ) as _i6.Stream<_i5.CommitComment>); - @override - _i6.Future<_i5.CommitComment> createCommitComment( - _i5.RepositorySlug? slug, - _i5.RepositoryCommit? commit, { - required String? body, - String? path, - int? position, - int? line, - }) => - (super.noSuchMethod( - Invocation.method( - #createCommitComment, - [ - slug, - commit, - ], - { - #body: body, - #path: path, - #position: position, - #line: line, - }, - ), - returnValue: _i6.Future<_i5.CommitComment>.value(_FakeCommitComment_32( - this, - Invocation.method( - #createCommitComment, - [ - slug, - commit, - ], - { - #body: body, - #path: path, - #position: position, - #line: line, - }, - ), - )), - ) as _i6.Future<_i5.CommitComment>); - @override - _i6.Future<_i5.CommitComment> getCommitComment( - _i5.RepositorySlug? slug, { - required int? id, - }) => - (super.noSuchMethod( - Invocation.method( - #getCommitComment, - [slug], - {#id: id}, - ), - returnValue: _i6.Future<_i5.CommitComment>.value(_FakeCommitComment_32( - this, - Invocation.method( - #getCommitComment, - [slug], - {#id: id}, - ), - )), - ) as _i6.Future<_i5.CommitComment>); - @override - _i6.Future<_i5.CommitComment> updateCommitComment( - _i5.RepositorySlug? slug, { - required int? id, - required String? body, - }) => - (super.noSuchMethod( - Invocation.method( - #updateCommitComment, - [slug], - { - #id: id, - #body: body, - }, - ), - returnValue: _i6.Future<_i5.CommitComment>.value(_FakeCommitComment_32( - this, - Invocation.method( - #updateCommitComment, - [slug], - { - #id: id, - #body: body, - }, - ), - )), - ) as _i6.Future<_i5.CommitComment>); - @override - _i6.Future deleteCommitComment( - _i5.RepositorySlug? slug, { - required int? id, - }) => - (super.noSuchMethod( - Invocation.method( - #deleteCommitComment, - [slug], - {#id: id}, - ), - returnValue: _i6.Future.value(false), - ) as _i6.Future); - @override - _i6.Stream<_i5.RepositoryCommit> listCommits( - _i5.RepositorySlug? slug, { - String? sha, - String? path, - String? author, - String? committer, - DateTime? since, - DateTime? until, - }) => - (super.noSuchMethod( - Invocation.method( - #listCommits, - [slug], - { - #sha: sha, - #path: path, - #author: author, - #committer: committer, - #since: since, - #until: until, - }, - ), - returnValue: _i6.Stream<_i5.RepositoryCommit>.empty(), - ) as _i6.Stream<_i5.RepositoryCommit>); - @override - _i6.Future<_i5.RepositoryCommit> getCommit( - _i5.RepositorySlug? slug, - String? sha, - ) => - (super.noSuchMethod( - Invocation.method( - #getCommit, - [ - slug, - sha, - ], - ), - returnValue: _i6.Future<_i5.RepositoryCommit>.value(_FakeRepositoryCommit_33( - this, - Invocation.method( - #getCommit, - [ - slug, - sha, - ], - ), - )), - ) as _i6.Future<_i5.RepositoryCommit>); - @override - _i6.Future getCommitDiff( - _i5.RepositorySlug? slug, - String? sha, - ) => - (super.noSuchMethod( - Invocation.method( - #getCommitDiff, - [ - slug, - sha, - ], - ), - returnValue: _i6.Future.value(''), - ) as _i6.Future); - @override - _i6.Future<_i5.GitHubComparison> compareCommits( - _i5.RepositorySlug? slug, - String? refBase, - String? refHead, - ) => - (super.noSuchMethod( - Invocation.method( - #compareCommits, - [ - slug, - refBase, - refHead, - ], - ), - returnValue: _i6.Future<_i5.GitHubComparison>.value(_FakeGitHubComparison_34( - this, - Invocation.method( - #compareCommits, - [ - slug, - refBase, - refHead, - ], - ), - )), - ) as _i6.Future<_i5.GitHubComparison>); - @override - _i6.Future<_i5.GitHubFile> getReadme( - _i5.RepositorySlug? slug, { - String? ref, - }) => - (super.noSuchMethod( - Invocation.method( - #getReadme, - [slug], - {#ref: ref}, - ), - returnValue: _i6.Future<_i5.GitHubFile>.value(_FakeGitHubFile_35( - this, - Invocation.method( - #getReadme, - [slug], - {#ref: ref}, - ), - )), - ) as _i6.Future<_i5.GitHubFile>); - @override - _i6.Future<_i5.RepositoryContents> getContents( - _i5.RepositorySlug? slug, - String? path, { - String? ref, - }) => - (super.noSuchMethod( - Invocation.method( - #getContents, - [ - slug, - path, - ], - {#ref: ref}, - ), - returnValue: _i6.Future<_i5.RepositoryContents>.value(_FakeRepositoryContents_36( - this, - Invocation.method( - #getContents, - [ - slug, - path, - ], - {#ref: ref}, - ), - )), - ) as _i6.Future<_i5.RepositoryContents>); - @override - _i6.Future<_i5.ContentCreation> createFile( - _i5.RepositorySlug? slug, - _i5.CreateFile? file, - ) => - (super.noSuchMethod( - Invocation.method( - #createFile, - [ - slug, - file, - ], - ), - returnValue: _i6.Future<_i5.ContentCreation>.value(_FakeContentCreation_37( - this, - Invocation.method( - #createFile, - [ - slug, - file, - ], - ), - )), - ) as _i6.Future<_i5.ContentCreation>); - @override - _i6.Future<_i5.ContentCreation> updateFile( - _i5.RepositorySlug? slug, - String? path, - String? message, - String? content, - String? sha, { - String? branch, - }) => - (super.noSuchMethod( - Invocation.method( - #updateFile, - [ - slug, - path, - message, - content, - sha, - ], - {#branch: branch}, - ), - returnValue: _i6.Future<_i5.ContentCreation>.value(_FakeContentCreation_37( - this, - Invocation.method( - #updateFile, - [ - slug, - path, - message, - content, - sha, - ], - {#branch: branch}, - ), - )), - ) as _i6.Future<_i5.ContentCreation>); - @override - _i6.Future<_i5.ContentCreation> deleteFile( - _i5.RepositorySlug? slug, - String? path, - String? message, - String? sha, - String? branch, - ) => - (super.noSuchMethod( - Invocation.method( - #deleteFile, - [ - slug, - path, - message, - sha, - branch, - ], - ), - returnValue: _i6.Future<_i5.ContentCreation>.value(_FakeContentCreation_37( - this, - Invocation.method( - #deleteFile, - [ - slug, - path, - message, - sha, - branch, - ], - ), - )), - ) as _i6.Future<_i5.ContentCreation>); - @override - _i6.Future getArchiveLink( - _i5.RepositorySlug? slug, - String? ref, { - String? format = r'tarball', - }) => - (super.noSuchMethod( - Invocation.method( - #getArchiveLink, - [ - slug, - ref, - ], - {#format: format}, - ), - returnValue: _i6.Future.value(), - ) as _i6.Future); - @override - _i6.Stream<_i5.Repository> listForks(_i5.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listForks, - [slug], - ), - returnValue: _i6.Stream<_i5.Repository>.empty(), - ) as _i6.Stream<_i5.Repository>); - @override - _i6.Future<_i5.Repository> createFork( - _i5.RepositorySlug? slug, [ - _i5.CreateFork? fork, - ]) => - (super.noSuchMethod( - Invocation.method( - #createFork, - [ - slug, - fork, - ], - ), - returnValue: _i6.Future<_i5.Repository>.value(_FakeRepository_28( - this, - Invocation.method( - #createFork, - [ - slug, - fork, - ], - ), - )), - ) as _i6.Future<_i5.Repository>); - @override - _i6.Stream<_i5.Hook> listHooks(_i5.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listHooks, - [slug], - ), - returnValue: _i6.Stream<_i5.Hook>.empty(), - ) as _i6.Stream<_i5.Hook>); - @override - _i6.Future<_i5.Hook> getHook( - _i5.RepositorySlug? slug, - int? id, - ) => - (super.noSuchMethod( - Invocation.method( - #getHook, - [ - slug, - id, - ], - ), - returnValue: _i6.Future<_i5.Hook>.value(_FakeHook_38( - this, - Invocation.method( - #getHook, - [ - slug, - id, - ], - ), - )), - ) as _i6.Future<_i5.Hook>); - @override - _i6.Future<_i5.Hook> createHook( - _i5.RepositorySlug? slug, - _i5.CreateHook? hook, - ) => - (super.noSuchMethod( - Invocation.method( - #createHook, - [ - slug, - hook, - ], - ), - returnValue: _i6.Future<_i5.Hook>.value(_FakeHook_38( - this, - Invocation.method( - #createHook, - [ - slug, - hook, - ], - ), - )), - ) as _i6.Future<_i5.Hook>); - @override - _i6.Future<_i5.Hook> editHook( - _i5.RepositorySlug? slug, - _i5.Hook? hookToEdit, { - String? configUrl, - String? configContentType, - String? configSecret, - bool? configInsecureSsl, - List? events, - List? addEvents, - List? removeEvents, - bool? active, - }) => - (super.noSuchMethod( - Invocation.method( - #editHook, - [ - slug, - hookToEdit, - ], - { - #configUrl: configUrl, - #configContentType: configContentType, - #configSecret: configSecret, - #configInsecureSsl: configInsecureSsl, - #events: events, - #addEvents: addEvents, - #removeEvents: removeEvents, - #active: active, - }, - ), - returnValue: _i6.Future<_i5.Hook>.value(_FakeHook_38( - this, - Invocation.method( - #editHook, - [ - slug, - hookToEdit, - ], - { - #configUrl: configUrl, - #configContentType: configContentType, - #configSecret: configSecret, - #configInsecureSsl: configInsecureSsl, - #events: events, - #addEvents: addEvents, - #removeEvents: removeEvents, - #active: active, - }, - ), - )), - ) as _i6.Future<_i5.Hook>); - @override - _i6.Future testPushHook( - _i5.RepositorySlug? slug, - int? id, - ) => - (super.noSuchMethod( - Invocation.method( - #testPushHook, - [ - slug, - id, - ], - ), - returnValue: _i6.Future.value(false), - ) as _i6.Future); - @override - _i6.Future pingHook( - _i5.RepositorySlug? slug, - int? id, - ) => - (super.noSuchMethod( - Invocation.method( - #pingHook, - [ - slug, - id, - ], - ), - returnValue: _i6.Future.value(false), - ) as _i6.Future); - @override - _i6.Future deleteHook( - _i5.RepositorySlug? slug, - int? id, - ) => - (super.noSuchMethod( - Invocation.method( - #deleteHook, - [ - slug, - id, - ], - ), - returnValue: _i6.Future.value(false), - ) as _i6.Future); - @override - _i6.Stream<_i5.PublicKey> listDeployKeys(_i5.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listDeployKeys, - [slug], - ), - returnValue: _i6.Stream<_i5.PublicKey>.empty(), - ) as _i6.Stream<_i5.PublicKey>); - @override - _i6.Future<_i5.PublicKey> getDeployKey( - _i5.RepositorySlug? slug, { - required int? id, - }) => - (super.noSuchMethod( - Invocation.method( - #getDeployKey, - [slug], - {#id: id}, - ), - returnValue: _i6.Future<_i5.PublicKey>.value(_FakePublicKey_39( - this, - Invocation.method( - #getDeployKey, - [slug], - {#id: id}, - ), - )), - ) as _i6.Future<_i5.PublicKey>); - @override - _i6.Future<_i5.PublicKey> createDeployKey( - _i5.RepositorySlug? slug, - _i5.CreatePublicKey? key, - ) => - (super.noSuchMethod( - Invocation.method( - #createDeployKey, - [ - slug, - key, - ], - ), - returnValue: _i6.Future<_i5.PublicKey>.value(_FakePublicKey_39( - this, - Invocation.method( - #createDeployKey, - [ - slug, - key, - ], - ), - )), - ) as _i6.Future<_i5.PublicKey>); - @override - _i6.Future deleteDeployKey({ - required _i5.RepositorySlug? slug, - required _i5.PublicKey? key, - }) => - (super.noSuchMethod( - Invocation.method( - #deleteDeployKey, - [], - { - #slug: slug, - #key: key, - }, - ), - returnValue: _i6.Future.value(false), - ) as _i6.Future); - @override - _i6.Future<_i5.RepositoryCommit> merge( - _i5.RepositorySlug? slug, - _i5.CreateMerge? merge, - ) => - (super.noSuchMethod( - Invocation.method( - #merge, - [ - slug, - merge, - ], - ), - returnValue: _i6.Future<_i5.RepositoryCommit>.value(_FakeRepositoryCommit_33( - this, - Invocation.method( - #merge, - [ - slug, - merge, - ], - ), - )), - ) as _i6.Future<_i5.RepositoryCommit>); - @override - _i6.Future<_i5.RepositoryPages> getPagesInfo(_i5.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #getPagesInfo, - [slug], - ), - returnValue: _i6.Future<_i5.RepositoryPages>.value(_FakeRepositoryPages_40( - this, - Invocation.method( - #getPagesInfo, - [slug], - ), - )), - ) as _i6.Future<_i5.RepositoryPages>); - @override - _i6.Stream<_i5.PageBuild> listPagesBuilds(_i5.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listPagesBuilds, - [slug], - ), - returnValue: _i6.Stream<_i5.PageBuild>.empty(), - ) as _i6.Stream<_i5.PageBuild>); - @override - _i6.Future<_i5.PageBuild> getLatestPagesBuild(_i5.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #getLatestPagesBuild, - [slug], - ), - returnValue: _i6.Future<_i5.PageBuild>.value(_FakePageBuild_41( - this, - Invocation.method( - #getLatestPagesBuild, - [slug], - ), - )), - ) as _i6.Future<_i5.PageBuild>); - @override - _i6.Stream<_i5.Release> listReleases(_i5.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listReleases, - [slug], - ), - returnValue: _i6.Stream<_i5.Release>.empty(), - ) as _i6.Stream<_i5.Release>); - @override - _i6.Future<_i5.Release> getLatestRelease(_i5.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #getLatestRelease, - [slug], - ), - returnValue: _i6.Future<_i5.Release>.value(_FakeRelease_42( - this, - Invocation.method( - #getLatestRelease, - [slug], - ), - )), - ) as _i6.Future<_i5.Release>); - @override - _i6.Future<_i5.Release> getReleaseById( - _i5.RepositorySlug? slug, - int? id, - ) => - (super.noSuchMethod( - Invocation.method( - #getReleaseById, - [ - slug, - id, - ], - ), - returnValue: _i6.Future<_i5.Release>.value(_FakeRelease_42( - this, - Invocation.method( - #getReleaseById, - [ - slug, - id, - ], - ), - )), - ) as _i6.Future<_i5.Release>); - @override - _i6.Future<_i5.Release> getReleaseByTagName( - _i5.RepositorySlug? slug, - String? tagName, - ) => - (super.noSuchMethod( - Invocation.method( - #getReleaseByTagName, - [ - slug, - tagName, - ], - ), - returnValue: _i6.Future<_i5.Release>.value(_FakeRelease_42( - this, - Invocation.method( - #getReleaseByTagName, - [ - slug, - tagName, - ], - ), - )), - ) as _i6.Future<_i5.Release>); - @override - _i6.Future<_i5.Release> createRelease( - _i5.RepositorySlug? slug, - _i5.CreateRelease? createRelease, { - bool? getIfExists = true, - }) => - (super.noSuchMethod( - Invocation.method( - #createRelease, - [ - slug, - createRelease, - ], - {#getIfExists: getIfExists}, - ), - returnValue: _i6.Future<_i5.Release>.value(_FakeRelease_42( - this, - Invocation.method( - #createRelease, - [ - slug, - createRelease, - ], - {#getIfExists: getIfExists}, - ), - )), - ) as _i6.Future<_i5.Release>); - @override - _i6.Future<_i5.Release> editRelease( - _i5.RepositorySlug? slug, - _i5.Release? releaseToEdit, { - String? tagName, - String? targetCommitish, - String? name, - String? body, - bool? draft, - bool? preRelease, - }) => - (super.noSuchMethod( - Invocation.method( - #editRelease, - [ - slug, - releaseToEdit, - ], - { - #tagName: tagName, - #targetCommitish: targetCommitish, - #name: name, - #body: body, - #draft: draft, - #preRelease: preRelease, - }, - ), - returnValue: _i6.Future<_i5.Release>.value(_FakeRelease_42( - this, - Invocation.method( - #editRelease, - [ - slug, - releaseToEdit, - ], - { - #tagName: tagName, - #targetCommitish: targetCommitish, - #name: name, - #body: body, - #draft: draft, - #preRelease: preRelease, - }, - ), - )), - ) as _i6.Future<_i5.Release>); - @override - _i6.Future deleteRelease( - _i5.RepositorySlug? slug, - _i5.Release? release, - ) => - (super.noSuchMethod( - Invocation.method( - #deleteRelease, - [ - slug, - release, - ], - ), - returnValue: _i6.Future.value(false), - ) as _i6.Future); - @override - _i6.Stream<_i5.ReleaseAsset> listReleaseAssets( - _i5.RepositorySlug? slug, - _i5.Release? release, - ) => - (super.noSuchMethod( - Invocation.method( - #listReleaseAssets, - [ - slug, - release, - ], - ), - returnValue: _i6.Stream<_i5.ReleaseAsset>.empty(), - ) as _i6.Stream<_i5.ReleaseAsset>); - @override - _i6.Future<_i5.ReleaseAsset> getReleaseAsset( - _i5.RepositorySlug? slug, - _i5.Release? release, { - required int? assetId, - }) => - (super.noSuchMethod( - Invocation.method( - #getReleaseAsset, - [ - slug, - release, - ], - {#assetId: assetId}, - ), - returnValue: _i6.Future<_i5.ReleaseAsset>.value(_FakeReleaseAsset_43( - this, - Invocation.method( - #getReleaseAsset, - [ - slug, - release, - ], - {#assetId: assetId}, - ), - )), - ) as _i6.Future<_i5.ReleaseAsset>); - @override - _i6.Future<_i5.ReleaseAsset> editReleaseAsset( - _i5.RepositorySlug? slug, - _i5.ReleaseAsset? assetToEdit, { - String? name, - String? label, - }) => - (super.noSuchMethod( - Invocation.method( - #editReleaseAsset, - [ - slug, - assetToEdit, - ], - { - #name: name, - #label: label, - }, - ), - returnValue: _i6.Future<_i5.ReleaseAsset>.value(_FakeReleaseAsset_43( - this, - Invocation.method( - #editReleaseAsset, - [ - slug, - assetToEdit, - ], - { - #name: name, - #label: label, - }, - ), - )), - ) as _i6.Future<_i5.ReleaseAsset>); - @override - _i6.Future deleteReleaseAsset( - _i5.RepositorySlug? slug, - _i5.ReleaseAsset? asset, - ) => - (super.noSuchMethod( - Invocation.method( - #deleteReleaseAsset, - [ - slug, - asset, - ], - ), - returnValue: _i6.Future.value(false), - ) as _i6.Future); - @override - _i6.Future> uploadReleaseAssets( - _i5.Release? release, - Iterable<_i5.CreateReleaseAsset>? createReleaseAssets, - ) => - (super.noSuchMethod( - Invocation.method( - #uploadReleaseAssets, - [ - release, - createReleaseAssets, - ], - ), - returnValue: _i6.Future>.value(<_i5.ReleaseAsset>[]), - ) as _i6.Future>); - @override - _i6.Future> listContributorStats(_i5.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listContributorStats, - [slug], - ), - returnValue: _i6.Future>.value(<_i5.ContributorStatistics>[]), - ) as _i6.Future>); - @override - _i6.Stream<_i5.YearCommitCountWeek> listCommitActivity(_i5.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listCommitActivity, - [slug], - ), - returnValue: _i6.Stream<_i5.YearCommitCountWeek>.empty(), - ) as _i6.Stream<_i5.YearCommitCountWeek>); - @override - _i6.Stream<_i5.WeeklyChangesCount> listCodeFrequency(_i5.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listCodeFrequency, - [slug], - ), - returnValue: _i6.Stream<_i5.WeeklyChangesCount>.empty(), - ) as _i6.Stream<_i5.WeeklyChangesCount>); - @override - _i6.Future<_i5.ContributorParticipation> getParticipation(_i5.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #getParticipation, - [slug], - ), - returnValue: _i6.Future<_i5.ContributorParticipation>.value(_FakeContributorParticipation_44( - this, - Invocation.method( - #getParticipation, - [slug], - ), - )), - ) as _i6.Future<_i5.ContributorParticipation>); - @override - _i6.Stream<_i5.PunchcardEntry> listPunchcard(_i5.RepositorySlug? slug) => (super.noSuchMethod( - Invocation.method( - #listPunchcard, - [slug], - ), - returnValue: _i6.Stream<_i5.PunchcardEntry>.empty(), - ) as _i6.Stream<_i5.PunchcardEntry>); - @override - _i6.Stream<_i5.RepositoryStatus> listStatuses( - _i5.RepositorySlug? slug, - String? ref, - ) => - (super.noSuchMethod( - Invocation.method( - #listStatuses, - [ - slug, - ref, - ], - ), - returnValue: _i6.Stream<_i5.RepositoryStatus>.empty(), - ) as _i6.Stream<_i5.RepositoryStatus>); - @override - _i6.Future<_i5.RepositoryStatus> createStatus( - _i5.RepositorySlug? slug, - String? ref, - _i5.CreateStatus? request, - ) => - (super.noSuchMethod( - Invocation.method( - #createStatus, - [ - slug, - ref, - request, - ], - ), - returnValue: _i6.Future<_i5.RepositoryStatus>.value(_FakeRepositoryStatus_45( - this, - Invocation.method( - #createStatus, - [ - slug, - ref, - request, - ], - ), - )), - ) as _i6.Future<_i5.RepositoryStatus>); - @override - _i6.Future<_i5.CombinedRepositoryStatus> getCombinedStatus( - _i5.RepositorySlug? slug, - String? ref, - ) => - (super.noSuchMethod( - Invocation.method( - #getCombinedStatus, - [ - slug, - ref, - ], - ), - returnValue: _i6.Future<_i5.CombinedRepositoryStatus>.value(_FakeCombinedRepositoryStatus_46( - this, - Invocation.method( - #getCombinedStatus, - [ - slug, - ref, - ], - ), - )), - ) as _i6.Future<_i5.CombinedRepositoryStatus>); - @override - _i6.Future<_i5.ReleaseNotes> generateReleaseNotes(_i5.CreateReleaseNotes? crn) => (super.noSuchMethod( - Invocation.method( - #generateReleaseNotes, - [crn], - ), - returnValue: _i6.Future<_i5.ReleaseNotes>.value(_FakeReleaseNotes_47( - this, - Invocation.method( - #generateReleaseNotes, - [crn], - ), - )), - ) as _i6.Future<_i5.ReleaseNotes>); -} - -/// A class which mocks [GitHubComparison]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockGitHubComparison extends _i1.Mock implements _i5.GitHubComparison { - MockGitHubComparison() { - _i1.throwOnMissingStub(this); - } - - @override - set url(String? _url) => super.noSuchMethod( - Invocation.setter( - #url, - _url, - ), - returnValueForMissingStub: null, - ); - @override - set status(String? _status) => super.noSuchMethod( - Invocation.setter( - #status, - _status, - ), - returnValueForMissingStub: null, - ); - @override - set aheadBy(int? _aheadBy) => super.noSuchMethod( - Invocation.setter( - #aheadBy, - _aheadBy, - ), - returnValueForMissingStub: null, - ); - @override - set behindBy(int? _behindBy) => super.noSuchMethod( - Invocation.setter( - #behindBy, - _behindBy, - ), - returnValueForMissingStub: null, - ); - @override - set totalCommits(int? _totalCommits) => super.noSuchMethod( - Invocation.setter( - #totalCommits, - _totalCommits, - ), - returnValueForMissingStub: null, - ); - @override - set files(List<_i5.CommitFile>? _files) => super.noSuchMethod( - Invocation.setter( - #files, - _files, - ), - returnValueForMissingStub: null, - ); - @override - set commits(List<_i5.RepositoryCommit>? _commits) => super.noSuchMethod( - Invocation.setter( - #commits, - _commits, - ), - returnValueForMissingStub: null, - ); - @override - Map toJson() => (super.noSuchMethod( - Invocation.method( - #toJson, - [], - ), - returnValue: {}, - ) as Map); -} - -/// A class which mocks [Response]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockResponse extends _i1.Mock implements _i2.Response { - MockResponse() { - _i1.throwOnMissingStub(this); - } - - @override - _i11.Uint8List get bodyBytes => (super.noSuchMethod( - Invocation.getter(#bodyBytes), - returnValue: _i11.Uint8List(0), - ) as _i11.Uint8List); - @override - String get body => (super.noSuchMethod( - Invocation.getter(#body), - returnValue: '', - ) as String); - @override - int get statusCode => (super.noSuchMethod( - Invocation.getter(#statusCode), - returnValue: 0, - ) as int); - @override - Map get headers => (super.noSuchMethod( - Invocation.getter(#headers), - returnValue: {}, - ) as Map); - @override - bool get isRedirect => (super.noSuchMethod( - Invocation.getter(#isRedirect), - returnValue: false, - ) as bool); - @override - bool get persistentConnection => (super.noSuchMethod( - Invocation.getter(#persistentConnection), - returnValue: false, - ) as bool); -} diff --git a/auto_submit/test/utilities/utils.dart b/auto_submit/test/utilities/utils.dart deleted file mode 100644 index 55e0cf836..000000000 --- a/auto_submit/test/utilities/utils.dart +++ /dev/null @@ -1,137 +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. - -import 'package:meta/meta.dart'; -import 'package:github/github.dart'; -import 'package:auto_submit/model/auto_submit_query_result.dart'; - -const String oid = '6dcb09b5b57875f334f61aebed695e2e4193db5e'; -const String title = 'some_title'; - -/// Helper for Github statuses. -@immutable -class StatusHelper { - const StatusHelper(this.name, this.state); - - static const StatusHelper flutterBuildSuccess = StatusHelper('tree-status', 'SUCCESS'); - static const StatusHelper flutterBuildFailure = StatusHelper('tree-status', 'FAILURE'); - static const StatusHelper otherStatusFailure = StatusHelper('other status', 'FAILURE'); - - final String name; - final String state; -} - -/// Helper to generate Github pull requests. -class PullRequestHelper { - PullRequestHelper({ - this.author = 'author1', - this.prNumber = 0, - this.repo = 'flutter', - this.authorAssociation = 'MEMBER', - this.title = 'some_title', - this.mergeableState = MergeableState.MERGEABLE, - this.reviews = const [ - PullRequestReviewHelper(authorName: 'member', state: ReviewState.APPROVED, memberType: MemberType.MEMBER), - ], - this.lastCommitHash = 'oid', - this.lastCommitStatuses = const [StatusHelper.flutterBuildSuccess], - this.lastCommitMessage = '', - this.dateTime, - this.mergedAt, - }); - - final int prNumber; - final String repo; - final String author; - final String authorAssociation; - final List reviews; - final String lastCommitHash; - List? lastCommitStatuses; - final String? lastCommitMessage; - final DateTime? dateTime; - final String title; - final MergeableState mergeableState; - final DateTime? mergedAt; - - RepositorySlug get slug => RepositorySlug('flutter', repo); - - Map toEntry() { - return { - 'author': {'login': author}, - 'authorAssociation': authorAssociation, - 'id': prNumber.toString(), - 'number': prNumber, - 'title': title, - 'mergeable': mergeableState.name, - 'mergedAt': (mergedAt ?? DateTime.now().subtract(const Duration(hours: 12))).toUtc().toIso8601String(), - 'reviews': { - 'nodes': reviews.map((PullRequestReviewHelper review) { - return { - 'author': {'login': review.authorName}, - 'authorAssociation': review.memberType.toString().replaceFirst('MemberType.', ''), - 'state': review.state.toString().replaceFirst('ReviewState.', ''), - }; - }).toList(), - }, - 'commits': { - 'nodes': [ - { - 'commit': { - 'oid': lastCommitHash, - 'pushedDate': (dateTime ?? DateTime.now().add(const Duration(hours: -2))).toUtc().toIso8601String(), - 'message': lastCommitMessage, - 'status': { - 'contexts': lastCommitStatuses != null - ? lastCommitStatuses!.map((StatusHelper status) { - return { - 'context': status.name, - 'state': status.state, - 'targetUrl': 'https://${status.name}', - }; - }).toList() - : [], - }, - }, - } - ], - }, - }; - } -} - -/// Generate `QueryResult` model as in auto submit. -QueryResult createQueryResult(PullRequestHelper pullRequest) { - return QueryResult.fromJson({ - 'repository': { - 'pullRequest': pullRequest.toEntry().cast(), - }, - }); -} - -/// List of review state from a github pull request. -enum ReviewState { - APPROVED, - CHANGES_REQUESTED, -} - -/// List of member type of a github pull request author/reviewer. -enum MemberType { - OWNER, - MEMBER, - OTHER, -} - -/// Details of a github pull request review. -@immutable -class PullRequestReviewHelper { - const PullRequestReviewHelper({ - required this.authorName, - required this.state, - required this.memberType, - }); - - final String authorName; - final ReviewState state; - final MemberType memberType; -} diff --git a/auto_submit/test/validations/approval_test.dart b/auto_submit/test/validations/approval_test.dart deleted file mode 100644 index 71884fa07..000000000 --- a/auto_submit/test/validations/approval_test.dart +++ /dev/null @@ -1,297 +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:auto_submit/configuration/repository_configuration.dart'; -import 'package:auto_submit/model/auto_submit_query_result.dart'; -import 'package:auto_submit/validations/approval.dart'; -import 'package:auto_submit/validations/validation.dart'; -import 'package:test/test.dart'; - -import 'package:github/github.dart' as gh; -import '../configuration/repository_configuration_data.dart'; -import '../requests/github_webhook_test_data.dart'; -import '../src/service/fake_config.dart'; -import '../src/service/fake_github_service.dart'; -import '../src/service/fake_graphql_client.dart'; -import '../utilities/mocks.mocks.dart'; -import 'approval_test_data.dart'; - -void main() { - late Approval approval; - late FakeConfig config; - final FakeGithubService githubService = FakeGithubService(); - late FakeGraphQLClient githubGraphQLClient; - final MockGitHub gitHub = MockGitHub(); - - setUp(() { - githubGraphQLClient = FakeGraphQLClient(); - config = FakeConfig(githubService: githubService, githubGraphQLClient: githubGraphQLClient, githubClient: gitHub); - config.repositoryConfigurationMock = RepositoryConfiguration.fromYaml(sampleConfigNoOverride); - approval = Approval(config: config); - }); - - group('Approval group tests', () { - Future computeValidationResult(String review) async { - final Map queryResultJsonDecode = jsonDecode(review) as Map; - final QueryResult queryResult = QueryResult.fromJson(queryResultJsonDecode); - final gh.PullRequest pullRequest = generatePullRequest(); - return approval.validate(queryResult, pullRequest); - } - - test('Author and reviewer in flutter-hackers, pr approved', () async { - final String review = constructSingleReviewerReview( - reviewState: 'APPROVED', - ); - - // githubService.isTeamMemberMockList = [true, true]; - githubService.isTeamMemberMockMap['author1'] = true; - githubService.isTeamMemberMockMap['keyonghan'] = true; - final ValidationResult result = await computeValidationResult(review); - - expect(result.result, isTrue); - expect(result.action, Action.REMOVE_LABEL); - expect(result.message.contains('This PR has met approval requirements for merging.'), isTrue); - }); - - test('Author is a NON member and reviewer is a member, need 1 more review', () async { - // githubService.isTeamMemberMockList = [true, true]; - githubService.isTeamMemberMockMap['author1'] = false; - githubService.isTeamMemberMockMap['keyonghan'] = true; - final String review = constructSingleReviewerReview( - reviewState: 'APPROVED', - ); - - final ValidationResult result = await computeValidationResult(review); - - expect(result.result, isFalse); - expect(result.action, Action.REMOVE_LABEL); - expect(result.message.contains('This PR has not met approval requirements for merging.'), isTrue); - expect(result.message.contains('need 1 more review'), isTrue); - }); - - test('Author is a NON member and reviewer is a NON member, need 2 more reviews', () async { - // githubService.isTeamMemberMockList = [true, true]; - githubService.isTeamMemberMockMap['author1'] = false; - githubService.isTeamMemberMockMap['keyonghan'] = false; - final String review = constructSingleReviewerReview( - reviewState: 'APPROVED', - ); - - final ValidationResult result = await computeValidationResult(review); - - expect(result.result, isFalse); - expect(result.action, Action.REMOVE_LABEL); - expect(result.message.contains('This PR has not met approval requirements for merging.'), isTrue); - expect(result.message.contains('need 2 more review'), isTrue); - }); - - test('Author is a member and reviewer is NON member, need 1 more review', () async { - // githubService.isTeamMemberMockList = [true, true]; - githubService.isTeamMemberMockMap['author1'] = true; - githubService.isTeamMemberMockMap['keyonghan'] = false; - final String review = constructSingleReviewerReview( - reviewState: 'APPROVED', - ); - - final ValidationResult result = await computeValidationResult(review); - - expect(result.result, isFalse); - expect(result.action, Action.IGNORE_TEMPORARILY); - expect(result.message.contains('This PR has not met approval requirements for merging.'), isTrue); - expect(result.message.contains('need 1 more review'), isTrue); - }); - - test('Author is NON member and reviewers are members, pr approved', () async { - githubService.isTeamMemberMockMap['author1'] = false; - githubService.isTeamMemberMockMap['author2'] = true; - githubService.isTeamMemberMockMap['author3'] = true; - final String review = constructTwoReviewerReview( - reviewState: 'APPROVED', - secondReviewState: 'APPROVED', - ); - - final ValidationResult result = await computeValidationResult(review); - - expect(result.result, isTrue); - expect(result.action, Action.REMOVE_LABEL); - expect(result.message.contains('This PR has met approval requirements for merging.'), isTrue); - }); - - test('Author is NON member and one reviewer is a NON member, need 1 more review', () async { - githubService.isTeamMemberMockMap['author1'] = false; - githubService.isTeamMemberMockMap['author2'] = true; - githubService.isTeamMemberMockMap['author3'] = false; - final String review = constructTwoReviewerReview( - reviewState: 'APPROVED', - secondReviewState: 'APPROVED', - ); - - final ValidationResult result = await computeValidationResult(review); - - expect(result.result, isFalse); - expect(result.action, Action.REMOVE_LABEL); - expect(result.message.contains('This PR has not met approval requirements for merging.'), isTrue); - expect(result.message.contains('need 1 more review'), isTrue); - }); - - test('Author is member and reviewers are NON members, need 1 more review', () async { - githubService.isTeamMemberMockMap['author1'] = true; - githubService.isTeamMemberMockMap['author2'] = false; - githubService.isTeamMemberMockMap['author3'] = false; - final String review = constructTwoReviewerReview( - reviewState: 'APPROVED', - secondReviewState: 'APPROVED', - ); - - final ValidationResult result = await computeValidationResult(review); - - expect(result.result, isFalse); - expect(result.action, Action.IGNORE_TEMPORARILY); - expect(result.message.contains('This PR has not met approval requirements for merging.'), isTrue); - expect(result.message.contains('need 1 more review'), isTrue); - }); - - test('Author is NON member and reviewers are NON members, need 2 reviews', () async { - githubService.isTeamMemberMockMap['author1'] = false; - githubService.isTeamMemberMockMap['author2'] = false; - githubService.isTeamMemberMockMap['author3'] = false; - final String review = constructTwoReviewerReview( - reviewState: 'APPROVED', - secondReviewState: 'APPROVED', - ); - - final ValidationResult result = await computeValidationResult(review); - - expect(result.result, isFalse); - expect(result.action, Action.REMOVE_LABEL); - expect(result.message.contains('This PR has not met approval requirements for merging.'), isTrue); - expect(result.message.contains('need 2 more review'), isTrue); - }); - - test('Verify author review count does not go negative', () async { - githubService.isTeamMemberMockMap['author1'] = false; - githubService.isTeamMemberMockMap['ricardoamador'] = false; - githubService.isTeamMemberMockMap['keyonghan'] = false; - githubService.isTeamMemberMockMap['nehalvpatel'] = false; - final String review = constructMultipleReviewerReview( - reviewState: 'APPROVED', - secondReviewState: 'APPROVED', - thirdReviewState: 'APPROVED', - ); - - final ValidationResult result = await computeValidationResult(review); - - expect(result.result, isFalse); - expect(result.action, Action.REMOVE_LABEL); - expect(result.message.contains('This PR has not met approval requirements for merging.'), isTrue); - expect(result.message.contains('need 2 more review'), isTrue); - }); - - test('Verify author review count does not go negative', () async { - githubService.isTeamMemberMockMap['author1'] = true; - githubService.isTeamMemberMockMap['ricardoamador'] = true; - githubService.isTeamMemberMockMap['keyonghan'] = true; - githubService.isTeamMemberMockMap['nehalvpatel'] = true; - final String review = constructMultipleReviewerReview( - reviewState: 'APPROVED', - secondReviewState: 'APPROVED', - thirdReviewState: 'APPROVED', - ); - - final ValidationResult result = await computeValidationResult(review); - - expect(result.result, isTrue); - expect(result.action, Action.REMOVE_LABEL); - expect(result.message.contains('This PR has met approval requirements for merging.'), isTrue); - }); - - test('Author is member and member requests changes, 1 review is needed', () async { - githubService.isTeamMemberMockMap['author1'] = true; - githubService.isTeamMemberMockMap['keyonghan'] = true; - final String review = constructSingleReviewerReview( - reviewState: 'CHANGES_REQUESTED', - ); - - final ValidationResult result = await computeValidationResult(review); - - expect(result.result, isFalse); - expect(result.action, Action.REMOVE_LABEL); - expect(result.message.contains('This PR has not met approval requirements for merging.'), isTrue); - expect(result.message.contains('Changes were requested by'), isTrue); - }); - - test('Author is member and two member reviews, 1 change request, review is not approved', () async { - githubService.isTeamMemberMockMap['author1'] = true; - githubService.isTeamMemberMockMap['author2'] = true; - githubService.isTeamMemberMockMap['author3'] = true; - final String review = constructTwoReviewerReview( - reviewState: 'CHANGES_REQUESTED', - secondReviewState: 'APPROVED', - ); - - final ValidationResult result = await computeValidationResult(review); - - expect(result.result, isFalse); - expect(result.action, Action.REMOVE_LABEL); - expect(result.message.contains('This PR has not met approval requirements for merging.'), isTrue); - expect(result.message.contains('Changes were requested by'), isTrue); - }); - - test('Multiple approving reviews from the same author are counted only 1 time.', () async { - githubService.isTeamMemberMockMap['author1'] = false; - githubService.isTeamMemberMockMap['ricardoamador'] = true; - final String review = constructTwoReviewerReview( - reviewState: 'APPROVED', - secondReviewState: 'APPROVED', - author: 'ricardoamador', - secondAuthor: 'ricardoamador', - ); - - final ValidationResult result = await computeValidationResult(review); - - expect(result.result, isFalse); - expect(result.action, Action.REMOVE_LABEL); - expect( - result.message.contains( - 'This PR has not met approval requirements for merging. You are not a member of flutter-hackers and need 1 more review(s) in order to merge this PR.', - ), - isTrue, - ); - }); - - test('Successful review overwrites previous changes requested.', () async { - githubService.isTeamMemberMockMap['author1'] = true; - githubService.isTeamMemberMockMap['keyonghan'] = true; - githubService.isTeamMemberMockMap['jmagman'] = true; - final ValidationResult result = await computeValidationResult(multipleReviewsSameAuthor); - - expect(result.result, isTrue); - expect(result.action, Action.REMOVE_LABEL); - expect(result.message.contains('This PR has met approval requirements for merging.'), isTrue); - }); - - test('Author cannot review own pr', () async { - githubService.isTeamMemberMockMap['author1'] = false; - githubService.isTeamMemberMockMap['author3'] = true; - final String review = constructTwoReviewerReview( - reviewState: 'APPROVED', - secondReviewState: 'APPROVED', - author: 'author1', - ); - - final ValidationResult result = await computeValidationResult(review); - - expect(result.result, isFalse); - expect(result.action, Action.REMOVE_LABEL); - expect( - result.message.contains( - 'This PR has not met approval requirements for merging. You are not a member of flutter-hackers', - ), - isTrue, - ); - }); - }); -} diff --git a/auto_submit/test/validations/approval_test_data.dart b/auto_submit/test/validations/approval_test_data.dart deleted file mode 100644 index b03ca3a7f..000000000 --- a/auto_submit/test/validations/approval_test_data.dart +++ /dev/null @@ -1,242 +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. - -String constructSingleReviewerReview({ - required String reviewState, -}) { - return ''' - { - "repository": { - "pullRequest": { - "author": { - "login": "author1" - }, - "id": "PR_kwDOA8VHis43rs4_", - "title": "[dependabot] Remove human reviewers", - "commits": { - "nodes":[ - { - "commit": { - "abbreviatedOid": "4009ecc", - "oid": "4009ecc0b6dbf5cb19cb97472147063e7368ec10", - "committedDate": "2022-05-11T22:35:02Z", - "pushedDate": "2022-05-11T22:35:03Z", - "status": { - "contexts":[ - { - "context":"tree-status", - "state":"SUCCESS", - "targetUrl":"https://ci.example.com/1000/output" - } - ] - } - } - } - ] - }, - "reviews": { - "nodes": [ - { - "author": { - "login": "keyonghan" - }, - "state": "$reviewState" - } - ] - } - } - } - } - '''; -} - -String constructTwoReviewerReview({ - required String reviewState, - required String secondReviewState, - String author = 'author2', - String secondAuthor = 'author3', -}) { - return ''' - { - "repository": { - "pullRequest": { - "author": { - "login": "author1" - }, - "id": "PR_kwDOA8VHis43rs4_", - "title": "[dependabot] Remove human reviewers", - "commits": { - "nodes":[ - { - "commit": { - "abbreviatedOid": "4009ecc", - "oid": "4009ecc0b6dbf5cb19cb97472147063e7368ec10", - "committedDate": "2022-05-11T22:35:02Z", - "pushedDate": "2022-05-11T22:35:03Z", - "status": { - "contexts":[ - { - "context":"tree-status", - "state":"SUCCESS", - "targetUrl":"https://ci.example.com/1000/output" - } - ] - } - } - } - ] - }, - "reviews": { - "nodes": [ - { - "author": { - "login": "$author" - }, - "state": "$reviewState" - }, - { - "author": { - "login": "$secondAuthor" - }, - "state": "$secondReviewState" - } - ] - } - } - } - } - '''; -} - -String constructMultipleReviewerReview({ - required String reviewState, - required String secondReviewState, - required String thirdReviewState, -}) { - return ''' - { - "repository": { - "pullRequest": { - "author": { - "login": "author1" - }, - "id": "PR_kwDOA8VHis43rs4_", - "title": "[dependabot] Remove human reviewers", - "commits": { - "nodes":[ - { - "commit": { - "abbreviatedOid": "4009ecc", - "oid": "4009ecc0b6dbf5cb19cb97472147063e7368ec10", - "committedDate": "2022-05-11T22:35:02Z", - "pushedDate": "2022-05-11T22:35:03Z", - "status": { - "contexts":[ - { - "context":"tree-status", - "state":"SUCCESS", - "targetUrl":"https://ci.example.com/1000/output" - } - ] - } - } - } - ] - }, - "reviews": { - "nodes": [ - { - "author": { - "login": "keyonghan" - }, - "state": "$reviewState" - }, - { - "author": { - "login": "ricardoamador" - }, - "state": "$secondReviewState" - }, - { - "author": { - "login": "nehalvpatel" - }, - "state": "$thirdReviewState" - } - ] - } - } - } - } - '''; -} - -const String multipleReviewsSameAuthor = ''' -{ - "repository": { - "pullRequest": { - "author": { - "login": "author1" - }, - "id": "PR_kwDOA8VHis43rs4_", - "title": "[dependabot] Remove human reviewers", - "commits": { - "nodes":[ - { - "commit": { - "abbreviatedOid": "4009ecc", - "oid": "4009ecc0b6dbf5cb19cb97472147063e7368ec10", - "committedDate": "2022-05-11T22:35:02Z", - "pushedDate": "2022-05-11T22:35:03Z", - "status": { - "contexts":[ - { - "context":"tree-status", - "state":"SUCCESS", - "targetUrl":"https://ci.example.com/1000/output" - } - ] - } - } - } - ] - }, - "reviews": { - "nodes": [ - { - "author": { - "login": "jmagman" - }, - "state": "COMMENTED" - }, - { - "author": { - "login": "keyonghan" - }, - "state": "COMMENTED" - }, - { - "author": { - "login": "jmagman" - }, - "state": "APPROVED" - }, - { - "author": { - "login": "jmagman" - }, - "state": "CHANGES_REQUESTED" - }, - { - "author": { - "login": "jmagman" - }, - "state": "APPROVED" - } - ] - } - } - } - } -'''; diff --git a/auto_submit/test/validations/ci_successful_test.dart b/auto_submit/test/validations/ci_successful_test.dart deleted file mode 100644 index 87f5d65e8..000000000 --- a/auto_submit/test/validations/ci_successful_test.dart +++ /dev/null @@ -1,515 +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. - -import 'dart:convert'; - -import 'ci_successful_test_data.dart'; - -import 'package:github/github.dart' as github; -import 'package:test/test.dart'; -import 'package:auto_submit/validations/ci_successful.dart'; -import 'package:auto_submit/model/auto_submit_query_result.dart'; -import 'package:auto_submit/validations/validation.dart'; -import 'package:auto_submit/configuration/repository_configuration.dart'; - -import '../utilities/utils.dart'; -import '../utilities/mocks.dart'; -import '../src/service/fake_config.dart'; -import '../src/service/fake_github_service.dart'; -import '../src/service/fake_graphql_client.dart'; -import '../requests/github_webhook_test_data.dart'; -import '../configuration/repository_configuration_data.dart'; - -void main() { - late CiSuccessful ciSuccessful; - late FakeConfig config; - FakeGithubService githubService = FakeGithubService(); - late FakeGraphQLClient githubGraphQLClient; - final MockGitHub gitHub = MockGitHub(); - late github.RepositorySlug slug; - late Set failures; - const int prNumber = 1; - - List getContextNodeListFromJson(final String repositoryStatuses) { - final List contextNodeList = []; - - final Map contextNodeMap = jsonDecode(repositoryStatuses) as Map; - - final dynamic statuses = contextNodeMap['statuses']; - for (Map map in statuses) { - contextNodeList.add(ContextNode.fromJson(map)); - } - - return contextNodeList; - } - - void convertContextNodeStatuses(List contextNodeList) { - for (ContextNode contextNode in contextNodeList) { - contextNode.state = contextNode.state!.toUpperCase(); - } - } - - /// Setup objects needed across test groups. - setUp(() { - githubGraphQLClient = FakeGraphQLClient(); - config = FakeConfig(githubService: githubService, githubGraphQLClient: githubGraphQLClient, githubClient: gitHub); - ciSuccessful = CiSuccessful(config: config); - slug = github.RepositorySlug('octocat', 'flutter'); - failures = {}; - config.repositoryConfigurationMock = RepositoryConfiguration.fromYaml(sampleConfigNoOverride); - }); - - group('validateCheckRuns', () { - test('ValidateCheckRuns no failures for skipped conclusion.', () { - githubService.checkRunsData = skippedCheckRunsMock; - final Future> checkRunFuture = githubService.getCheckRuns(slug, 'ref'); - const bool allSuccess = true; - - checkRunFuture.then((checkRuns) { - expect(ciSuccessful.validateCheckRuns(slug, prNumber, checkRuns, failures, allSuccess), isTrue); - expect(failures, isEmpty); - }); - }); - - test('ValidateCheckRuns no failures for successful conclusion.', () { - githubService.checkRunsData = checkRunsMock; - final Future> checkRunFuture = githubService.getCheckRuns(slug, 'ref'); - const bool allSuccess = true; - - checkRunFuture.then((checkRuns) { - expect(ciSuccessful.validateCheckRuns(slug, prNumber, checkRuns, failures, allSuccess), isTrue); - expect(failures, isEmpty); - }); - }); - - test('ValidateCheckRuns no failure for status completed and neutral conclusion.', () { - githubService.checkRunsData = neutralCheckRunsMock; - final Future> checkRunFuture = githubService.getCheckRuns(slug, 'ref'); - const bool allSuccess = true; - - checkRunFuture.then((checkRuns) { - expect(ciSuccessful.validateCheckRuns(slug, prNumber, checkRuns, failures, allSuccess), isTrue); - expect(failures, isEmpty); - }); - }); - - test('ValidateCheckRuns failure detected on status completed no neutral conclusion.', () { - githubService.checkRunsData = failedCheckRunsMock; - final Future> checkRunFuture = githubService.getCheckRuns(slug, 'ref'); - const bool allSuccess = true; - - checkRunFuture.then((checkRuns) { - expect(ciSuccessful.validateCheckRuns(slug, prNumber, checkRuns, failures, allSuccess), isFalse); - expect(failures, isNotEmpty); - expect(failures.length, 1); - }); - }); - - test('ValidateCheckRuns succes with multiple successful check runs.', () { - githubService.checkRunsData = multipleCheckRunsMock; - final Future> checkRunFuture = githubService.getCheckRuns(slug, 'ref'); - const bool allSuccess = true; - - checkRunFuture.then((checkRuns) { - expect(ciSuccessful.validateCheckRuns(slug, prNumber, checkRuns, failures, allSuccess), isTrue); - expect(failures, isEmpty); - }); - }); - - test('ValidateCheckRuns failed with multiple check runs.', () { - githubService.checkRunsData = multipleCheckRunsWithFailureMock; - final Future> checkRunFuture = githubService.getCheckRuns(slug, 'ref'); - const bool allSuccess = true; - - checkRunFuture.then((checkRuns) { - expect(ciSuccessful.validateCheckRuns(slug, prNumber, checkRuns, failures, allSuccess), isFalse); - expect(failures, isNotEmpty); - expect(failures.length, 1); - }); - }); - - test('ValidateCheckRuns allSucces false but no failures recorded.', () { - /// This test just checks that a checkRun that has not yet completed and - /// does not cause failure is a candidate to be temporarily ignored. - githubService.checkRunsData = inprogressAndNotFailedCheckRunMock; - final Future> checkRunFuture = githubService.getCheckRuns(slug, 'ref'); - const bool allSuccess = true; - - checkRunFuture.then((checkRuns) { - expect(ciSuccessful.validateCheckRuns(slug, prNumber, checkRuns, failures, allSuccess), isFalse); - expect(failures, isEmpty); - }); - }); - - test('ValidateCheckRuns allSuccess false is preserved.', () { - githubService.checkRunsData = multipleCheckRunsWithFailureMock; - final Future> checkRunFuture = githubService.getCheckRuns(slug, 'ref'); - const bool allSuccess = false; - - checkRunFuture.then((checkRuns) { - expect(ciSuccessful.validateCheckRuns(slug, prNumber, checkRuns, failures, allSuccess), isFalse); - expect(failures, isNotEmpty); - expect(failures.length, 1); - }); - }); - }); - - group('validateStatuses', () { - test('Validate successful statuses show as successful.', () { - final List contextNodeList = getContextNodeListFromJson(repositoryStatusesMock); - const bool allSuccess = true; - final Author author = Author(login: 'ricardoamador'); - - /// The status must be uppercase as the original code is expecting this. - convertContextNodeStatuses(contextNodeList); - expect(ciSuccessful.validateStatuses(slug, prNumber, author, [], contextNodeList, failures, allSuccess), isTrue); - expect(failures, isEmpty); - }); - - test('Validate statuses that are not successful but do not cause failure.', () { - final List contextNodeList = getContextNodeListFromJson(failedAuthorsStatusesMock); - const bool allSuccess = true; - final Author author = Author(login: 'ricardoamador'); - - final List labelNames = []; - labelNames.add('warning: land on red to fix tree breakage'); - labelNames.add('Other label'); - - convertContextNodeStatuses(contextNodeList); - expect( - ciSuccessful.validateStatuses(slug, prNumber, author, labelNames, contextNodeList, failures, allSuccess), - isTrue, - ); - expect(failures, isEmpty); - }); - - test('Validate failure statuses do not cause failure with not in authors control.', () { - final List contextNodeList = getContextNodeListFromJson(failedAuthorsStatusesMock); - const bool allSuccess = true; - final Author author = Author(login: 'ricardoamador'); - - final List labelNames = []; - labelNames.add('Compelling label'); - labelNames.add('Another Compelling label'); - - convertContextNodeStatuses(contextNodeList); - expect( - ciSuccessful.validateStatuses(slug, prNumber, author, labelNames, contextNodeList, failures, allSuccess), - isFalse, - ); - expect(failures, isEmpty); - }); - - test('Validate failure statuses cause failures with not in authors control.', () { - final List contextNodeList = getContextNodeListFromJson(failedNonAuthorsStatusesMock); - const bool allSuccess = true; - final Author author = Author(login: 'ricardoamador'); - - final List labelNames = []; - labelNames.add('Compelling label'); - labelNames.add('Another Compelling label'); - - convertContextNodeStatuses(contextNodeList); - expect( - ciSuccessful.validateStatuses(slug, prNumber, author, labelNames, contextNodeList, failures, allSuccess), - isFalse, - ); - expect(failures, isNotEmpty); - expect(failures.length, 2); - }); - - test('Validate failure statuses cause failures and preserves false allSuccess.', () { - final List contextNodeList = getContextNodeListFromJson(failedNonAuthorsStatusesMock); - const bool allSuccess = false; - final Author author = Author(login: 'ricardoamador'); - - final List labelNames = []; - labelNames.add('Compelling label'); - labelNames.add('Another Compelling label'); - - convertContextNodeStatuses(contextNodeList); - expect( - ciSuccessful.validateStatuses(slug, prNumber, author, labelNames, contextNodeList, failures, allSuccess), - isFalse, - ); - expect(failures, isNotEmpty); - expect(failures.length, 2); - }); - - test('Validate flutter-gold is not checked for engine auto roller pull requests.', () { - final List contextNodeList = getContextNodeListFromJson(repositoryStatusesWithGoldMock); - const bool allSuccess = true; - final Author author = Author(login: 'skia-flutter-autoroll'); - slug = github.RepositorySlug('flutter', 'engine'); - - final List labelNames = []; - labelNames.add('Compelling label'); - labelNames.add('Another Compelling label'); - - convertContextNodeStatuses(contextNodeList); - expect( - ciSuccessful.validateStatuses(slug, prNumber, author, labelNames, contextNodeList, failures, allSuccess), - isTrue, - ); - expect(failures, isEmpty); - expect(failures.length, 0); - }); - - test('Validate flutter-gold is not checked even if failing for engine auto roller pull requests.', () { - final List contextNodeList = getContextNodeListFromJson(repositoryStatusesWithFailedGoldMock); - const bool allSuccess = true; - final Author author = Author(login: 'skia-flutter-autoroll'); - slug = github.RepositorySlug('flutter', 'engine'); - - final List labelNames = []; - labelNames.add('Compelling label'); - labelNames.add('Another Compelling label'); - - convertContextNodeStatuses(contextNodeList); - expect( - ciSuccessful.validateStatuses(slug, prNumber, author, labelNames, contextNodeList, failures, allSuccess), - isTrue, - ); - expect(failures, isEmpty); - expect(failures.length, 0); - }); - - test('Validate flutter-gold is checked for non engine auto roller pull requests.', () { - final List contextNodeList = getContextNodeListFromJson(repositoryStatusesWithFailedGoldMock); - const bool allSuccess = true; - final Author author = Author(login: 'ricardoamador'); - slug = github.RepositorySlug('flutter', 'engine'); - - final List labelNames = []; - labelNames.add('Compelling label'); - labelNames.add('Another Compelling label'); - - convertContextNodeStatuses(contextNodeList); - expect( - ciSuccessful.validateStatuses(slug, prNumber, author, labelNames, contextNodeList, failures, allSuccess), - isFalse, - ); - expect(failures, isNotEmpty); - expect(failures.length, 1); - }); - }); - - group('treeStatusCheck', () { - test('Validate tree status is set contains slug.', () { - slug = github.RepositorySlug('flutter', 'flutter'); - final List contextNodeList = getContextNodeListFromJson(repositoryStatusesMock); - expect(contextNodeList.isEmpty, false); - - /// The status must be uppercase as the original code is expecting this. - convertContextNodeStatuses(contextNodeList); - final bool treeStatusFlag = ciSuccessful.treeStatusCheck(slug, prNumber, contextNodeList); - expect(treeStatusFlag, true); - }); - - test('Validate tree status is set does not contain slug.', () { - slug = github.RepositorySlug('flutter', 'infra'); - final List contextNodeList = getContextNodeListFromJson(repositoryStatusesMock); - expect(contextNodeList.isEmpty, false); - - /// The status must be uppercase as the original code is expecting this. - convertContextNodeStatuses(contextNodeList); - final bool treeStatusFlag = ciSuccessful.treeStatusCheck(slug, prNumber, contextNodeList); - expect(treeStatusFlag, true); - }); - - test('Validate tree status is set but context does not match slug.', () { - slug = github.RepositorySlug('flutter', 'flutter'); - final List contextNodeList = getContextNodeListFromJson(repositoryStatusesNonLuciFlutterMock); - - /// The status must be uppercase as the original code is expecting this. - convertContextNodeStatuses(contextNodeList); - final bool treeStatusFlag = ciSuccessful.treeStatusCheck(slug, prNumber, contextNodeList); - expect(treeStatusFlag, false); - }); - }); - - group('validate', () { - test('Commit has a null status, no statuses to verify.', () { - final Map queryResultJsonDecode = - jsonDecode(nullStatusCommitRepositoryJson) as Map; - final QueryResult queryResult = QueryResult.fromJson(queryResultJsonDecode); - expect(queryResult, isNotNull); - final PullRequest pr = queryResult.repository!.pullRequest!; - expect(pr, isNotNull); - final Commit commit = pr.commits!.nodes!.single.commit!; - expect(commit, isNotNull); - expect(commit.status, isNull); - - final github.PullRequest npr = generatePullRequest(); - githubService.checkRunsData = checkRunsMock; - - ciSuccessful.validate(queryResult, npr).then((value) { - // fails because in this case there is only a single fail status - expect(false, value.result); - // Remove label. - expect(value.action, Action.IGNORE_TEMPORARILY); - expect(value.message, 'Hold to wait for the tree status ready.'); - }); - }); - - test('Commit has no statuses to verify.', () { - final Map queryResultJsonDecode = jsonDecode(noStatusInCommitJson) as Map; - final QueryResult queryResult = QueryResult.fromJson(queryResultJsonDecode); - expect(queryResult, isNotNull); - final PullRequest pr = queryResult.repository!.pullRequest!; - expect(pr, isNotNull); - - final github.PullRequest npr = generatePullRequest(); - githubService.checkRunsData = checkRunsMock; - - ciSuccessful.validate(queryResult, npr).then((value) { - // fails because in this case there is only a single fail status - expect(false, value.result); - // Remove label. - expect(value.action, Action.IGNORE_TEMPORARILY); - expect(value.message, 'Hold to wait for the tree status ready.'); - }); - }); - - // When branch is default we need to wait for the tree status if it is not - // present. - test('Commit has no statuses to verify.', () { - final Map queryResultJsonDecode = jsonDecode(noStatusInCommitJson) as Map; - final QueryResult queryResult = QueryResult.fromJson(queryResultJsonDecode); - expect(queryResult, isNotNull); - final PullRequest pr = queryResult.repository!.pullRequest!; - expect(pr, isNotNull); - - final github.PullRequest npr = generatePullRequest(); - githubService.checkRunsData = checkRunsMock; - - ciSuccessful.validate(queryResult, npr).then((value) { - // fails because in this case there is only a single fail status - expect(false, value.result); - // Remove label. - expect(value.action, Action.IGNORE_TEMPORARILY); - expect(value.message, 'Hold to wait for the tree status ready.'); - }); - }); - - // Test for when the base branch is not default, we should not check the - // tree status as it does not apply. - test('Commit has no statuses to verify and base branch is not default.', () { - final Map queryResultJsonDecode = jsonDecode(noStatusInCommitJson) as Map; - final QueryResult queryResult = QueryResult.fromJson(queryResultJsonDecode); - expect(queryResult, isNotNull); - final PullRequest pr = queryResult.repository!.pullRequest!; - expect(pr, isNotNull); - - final github.PullRequest npr = generatePullRequest(baseRef: 'test_feature'); - githubService.checkRunsData = checkRunsMock; - - ciSuccessful.validate(queryResult, npr).then((value) { - expect(true, value.result); - // Remove label. - expect(value.action, Action.REMOVE_LABEL); - expect(value.message, isEmpty); - }); - }); - - test('Commit has statuses to verify, action remove label, no message.', () { - final Map queryResultJsonDecode = - jsonDecode(nonNullStatusSUCCESSCommitRepositoryJson) as Map; - final QueryResult queryResult = QueryResult.fromJson(queryResultJsonDecode); - expect(queryResult, isNotNull); - final PullRequest pr = queryResult.repository!.pullRequest!; - expect(pr, isNotNull); - final Commit commit = pr.commits!.nodes!.single.commit!; - expect(commit, isNotNull); - expect(commit.status, isNotNull); - - final github.PullRequest npr = generatePullRequest(); - githubService.checkRunsData = checkRunsMock; - - ciSuccessful.validate(queryResult, npr).then((value) { - // No failure. - expect(value.result, isTrue); - // Remove label. - expect((value.action == Action.REMOVE_LABEL), isTrue); - expect(value.message, isEmpty); - }); - }); - - test('Commit has statuses to verify, action ignore failure, no message.', () { - final Map queryResultJsonDecode = - jsonDecode(nonNullStatusFAILURECommitRepositoryJson) as Map; - final QueryResult queryResult = QueryResult.fromJson(queryResultJsonDecode); - expect(queryResult, isNotNull); - final PullRequest pr = queryResult.repository!.pullRequest!; - expect(pr, isNotNull); - final Commit commit = pr.commits!.nodes!.single.commit!; - expect(commit, isNotNull); - expect(commit.status, isNotNull); - - final github.PullRequest npr = generatePullRequest(labelName: 'warning: land on red to fix tree breakage'); - githubService.checkRunsData = checkRunsMock; - - ciSuccessful.validate(queryResult, npr).then((value) { - // No failure. - expect(value.result, isTrue); - // Remove label. - expect((value.action == Action.IGNORE_FAILURE), isTrue); - expect(value.message, isEmpty); - }); - }); - - test('Commit has statuses to verify, action failure, no message.', () { - final Map queryResultJsonDecode = - jsonDecode(nonNullStatusFAILURECommitRepositoryJson) as Map; - final QueryResult queryResult = QueryResult.fromJson(queryResultJsonDecode); - expect(queryResult, isNotNull); - final PullRequest pr = queryResult.repository!.pullRequest!; - expect(pr, isNotNull); - final Commit commit = pr.commits!.nodes!.single.commit!; - expect(commit, isNotNull); - expect(commit.status, isNotNull); - - final github.PullRequest npr = generatePullRequest(); - githubService.checkRunsData = checkRunsMock; - - ciSuccessful.validate(queryResult, npr).then((value) { - // No failure. - expect(false, value.result); - // Remove label. - expect((value.action == Action.IGNORE_TEMPORARILY), isTrue); - expect(value.message, isEmpty); - }); - }); - }); - - group('Validate empty message is not returned.', () { - setUp(() { - githubService = FakeGithubService(client: MockGitHub()); - config = FakeConfig(githubService: githubService); - ciSuccessful = CiSuccessful(config: config); - slug = github.RepositorySlug('flutter', 'cocoon'); - config.repositoryConfigurationMock = RepositoryConfiguration.fromYaml(sampleConfigNoOverride); - }); - - test('returns correct message when validation fails', () async { - final PullRequestHelper flutterRequest = PullRequestHelper( - prNumber: 0, - lastCommitHash: oid, - reviews: [], - ); - - githubService.checkRunsData = failedCheckRunsMock; - final github.PullRequest pullRequest = generatePullRequest(prNumber: 0, repoName: slug.name); - final QueryResult queryResult = createQueryResult(flutterRequest); - - final ValidationResult validationResult = await ciSuccessful.validate(queryResult, pullRequest); - - expect(validationResult.result, false); - expect( - validationResult.message, - '- The status or check suite [failed_checkrun](https://example.com) has failed. Please fix the issues identified (or deflake) before re-applying this label.\n', - ); - }); - }); -} diff --git a/auto_submit/test/validations/ci_successful_test_data.dart b/auto_submit/test/validations/ci_successful_test_data.dart deleted file mode 100644 index 9638790fa..000000000 --- a/auto_submit/test/validations/ci_successful_test_data.dart +++ /dev/null @@ -1,179 +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. - -const String noStatusInCommitJson = ''' -{ - "repository": { - "pullRequest": { - "author": { - "login": "author1" - }, - "authorAssociation": "MEMBER", - "id": "PR_kwDOA8VHis43rs4_", - "title": "[dependabot] Remove human reviewers", - "commits": { - "nodes":[ - { - "commit": { - "abbreviatedOid": "4009ecc", - "oid": "4009ecc0b6dbf5cb19cb97472147063e7368ec10", - "committedDate": "2022-05-11T22:35:02Z", - "pushedDate": "2022-05-11T22:35:03Z", - "status": { - "contexts":[ - - ] - } - } - } - ] - }, - "reviews": { - "nodes": [ - { - "author": { - "login": "keyonghan" - }, - "authorAssociation": "MEMBER", - "state": "APPROVED" - } - ] - } - } - } - } -'''; - -const String nullStatusCommitRepositoryJson = ''' - { - "repository": { - "pullRequest": { - "author": { - "login": "author1" - }, - "authorAssociation": "MEMBER", - "id": "PR_kwDOA8VHis43rs4_", - "title": "[dependabot] Remove human reviewers", - "commits": { - "nodes":[ - { - "commit": { - "abbreviatedOid": "4009ecc", - "oid": "4009ecc0b6dbf5cb19cb97472147063e7368ec10", - "committedDate": "2022-05-11T22:35:02Z", - "pushedDate": "2022-05-11T22:35:03Z", - "status": null - } - } - ] - }, - "reviews": { - "nodes": [ - { - "author": { - "login": "keyonghan" - }, - "authorAssociation": "MEMBER", - "state": "APPROVED" - } - ] - } - } - } - } - '''; - -const String nonNullStatusSUCCESSCommitRepositoryJson = ''' - { - "repository": { - "pullRequest": { - "author": { - "login": "author1" - }, - "authorAssociation": "MEMBER", - "id": "PR_kwDOA8VHis43rs4_", - "title": "[dependabot] Remove human reviewers", - "commits": { - "nodes":[ - { - "commit": { - "abbreviatedOid": "4009ecc", - "oid": "4009ecc0b6dbf5cb19cb97472147063e7368ec10", - "committedDate": "2022-05-11T22:35:02Z", - "pushedDate": "2022-05-11T22:35:03Z", - "status": { - "contexts":[ - { - "context":"tree-status", - "state":"SUCCESS", - "targetUrl":"https://ci.example.com/1000/output" - } - ] - } - } - } - ] - }, - "reviews": { - "nodes": [ - { - "author": { - "login": "keyonghan" - }, - "authorAssociation": "MEMBER", - "state": "APPROVED" - } - ] - } - } - } - } - '''; - -const String nonNullStatusFAILURECommitRepositoryJson = ''' - { - "repository": { - "pullRequest": { - "author": { - "login": "author1" - }, - "authorAssociation": "MEMBER", - "id": "PR_kwDOA8VHis43rs4_", - "title": "[dependabot] Remove human reviewers", - "commits": { - "nodes":[ - { - "commit": { - "abbreviatedOid": "4009ecc", - "oid": "4009ecc0b6dbf5cb19cb97472147063e7368ec10", - "committedDate": "2022-05-11T22:35:02Z", - "pushedDate": "2022-05-11T22:35:03Z", - "status": { - "contexts":[ - { - "context":"tree-status", - "state":"FAILURE", - "targetUrl":"https://ci.example.com/1000/output" - } - ] - } - } - } - ] - }, - "reviews": { - "nodes": [ - { - "author": { - "login": "keyonghan" - }, - "authorAssociation": "MEMBER", - "state": "APPROVED" - } - ] - } - } - } - } - '''; diff --git a/auto_submit/test/validations/mergeable_test.dart b/auto_submit/test/validations/mergeable_test.dart deleted file mode 100644 index a75c20699..000000000 --- a/auto_submit/test/validations/mergeable_test.dart +++ /dev/null @@ -1,112 +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:auto_submit/model/auto_submit_query_result.dart' as auto; -import 'package:auto_submit/validations/mergeable.dart'; -import 'package:auto_submit/validations/validation.dart'; -import 'package:github/github.dart' as github; -import 'package:test/test.dart'; - -import '../requests/github_webhook_test_data.dart'; -import '../src/service/fake_config.dart'; -import '../src/service/fake_github_service.dart'; -import '../src/service/fake_graphql_client.dart'; -import '../utilities/mocks.dart'; -import '../utilities/utils.dart'; - -void main() { - late Mergeable mergeable; - late FakeConfig config; - late FakeGithubService githubService; - late FakeGraphQLClient githubGraphQLClient; - - setUp(() { - githubGraphQLClient = FakeGraphQLClient(); - githubService = FakeGithubService(client: MockGitHub()); - config = FakeConfig(githubService: githubService, githubGraphQLClient: githubGraphQLClient); - mergeable = Mergeable(config: config); - }); - - test('Pull request is mergeable', () async { - const String org = 'flutter'; - const String repo = 'flutter'; - - final PullRequestHelper flutterRequest = PullRequestHelper( - prNumber: 0, - lastCommitHash: oid, - reviews: [], - ); - final auto.QueryResult queryResult = createQueryResult(flutterRequest); - - final github.PullRequest pullRequest = generatePullRequest( - mergeable: true, - login: org, - repoName: repo, - ); - - final ValidationResult processMergeResult = await mergeable.validate( - queryResult, - pullRequest, - ); - expect(processMergeResult.result, isTrue); - expect(processMergeResult.message, 'Pull request flutter/flutter/1347 is mergeable'); - }); - - test('Pull request mergeability has not been determined', () async { - const String org = 'flutter'; - const String repo = 'flutter'; - - final PullRequestHelper flutterRequest = PullRequestHelper( - prNumber: 0, - lastCommitHash: oid, - reviews: [], - mergeableState: auto.MergeableState.UNKNOWN, - ); - final auto.QueryResult queryResult = createQueryResult(flutterRequest); - - final github.PullRequest pullRequest = generatePullRequest( - mergeable: null, - login: org, - repoName: repo, - ); - githubService.pullRequestData = pullRequest; - - final ValidationResult processMergeResult = await mergeable.validate( - queryResult, - pullRequest, - ); - expect(processMergeResult.result, isFalse); - expect( - processMergeResult.message, - 'Mergeability of pull request flutter/flutter/1347 could not be determined at time of merge.', - ); - }); - - test('Pull request cannot be merged', () async { - const String org = 'flutter'; - const String repo = 'flutter'; - - final github.PullRequest pullRequest = generatePullRequest( - mergeable: false, - login: org, - repoName: repo, - ); - githubService.pullRequestData = pullRequest; - - final PullRequestHelper flutterRequest = PullRequestHelper( - prNumber: 0, - lastCommitHash: oid, - reviews: [], - mergeableState: auto.MergeableState.CONFLICTING, - ); - final auto.QueryResult queryResult = createQueryResult(flutterRequest); - - final ValidationResult processMergeResult = await mergeable.validate( - queryResult, - pullRequest, - ); - expect(processMergeResult.result, isFalse); - expect(processMergeResult.message, 'Pull request flutter/flutter/1347 is not in a mergeable state.'); - }); -} diff --git a/auto_submit/test/validations/revert_test_data.dart b/auto_submit/test/validations/revert_test_data.dart deleted file mode 100644 index 24de131b2..000000000 --- a/auto_submit/test/validations/revert_test_data.dart +++ /dev/null @@ -1,767 +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. - -const String queryResultRepositoryOwnerJson = ''' -{ - "repository": { - "pullRequest": { - "author": { - "login": "author1" - }, - "id": "PR_kwDOA8VHis43rs4_", - "title": "Revert Trailing comma analysis", - "commits": { - "nodes":[ - { - "commit": { - "abbreviatedOid": "4009ecc", - "oid": "4009ecc0b6dbf5cb19cb97472147063e7368ec10", - "committedDate": "2022-05-11T22:35:02Z", - "pushedDate": "2022-05-11T22:35:03Z", - "status": { - "contexts":[ - - ] - } - } - } - ] - }, - "reviews": { - - } - } - } - } -'''; - -const String queryResultRepositoryContributorJson = ''' -{ - "repository": { - "pullRequest": { - "author": { - "login": "author1" - }, - "id": "PR_kwDOA8VHis43rs4_", - "title": "Revert Trailing comma analysis", - "commits": { - "nodes":[ - { - "commit": { - "abbreviatedOid": "4009ecc", - "oid": "4009ecc0b6dbf5cb19cb97472147063e7368ec10", - "committedDate": "2022-05-11T22:35:02Z", - "pushedDate": "2022-05-11T22:35:03Z", - "status": { - "contexts":[ - - ] - } - } - } - ] - }, - "reviews": { - - } - } - } - } -'''; - -const String revertPullRequestJson = ''' - { - "url": "https://api.github.com/repos/flutter/cocoon/pulls/2047", - "id": 1023297178, - "node_id": "PR_kwDOA8VHis48_kaa", - "number": 2047, - "state": "open", - "locked": false, - "title": "Revert Trailing comma analysis", - "user": { - "login": "ricardoamador", - "id": 32242716 - }, - "body": "Reverts flutter/cocoon#2024", - "created_at": "2022-08-10T23:27:49Z", - "updated_at": "2022-08-12T16:57:35Z", - "labels": [], - "head": { - "label": "flutter:revert-2024-trailing_comma_analysis", - "ref": "revert-2024-trailing_comma_analysis", - "sha": "8c8d14b5eda928e25b43fc76da94f78ab11d99d7", - "user": { - "login": "flutter", - "id": 14101776, - "node_id": "MDEyOk9yZ2FuaXphdGlvbjE0MTAxNzc2" - }, - "repo": { - "id": 63260554, - "node_id": "MDEwOlJlcG9zaXRvcnk2MzI2MDU1NA==", - "name": "cocoon", - "full_name": "flutter/cocoon", - "private": false, - "owner": { - "login": "flutter", - "id": 14101776, - "node_id": "MDEyOk9yZ2FuaXphdGlvbjE0MTAxNzc2", - "avatar_url": "https://avatars.githubusercontent.com/u/14101776?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/flutter", - "html_url": "https://github.com/flutter", - "type": "Organization", - "site_admin": false - }, - "default_branch": "main" - } - }, - "base": { - "label": "flutter:main", - "ref": "main", - "sha": "04a33c0719e114a8afffb2e31e29c38ac6c9f0a7", - "user": { - "login": "flutter", - "id": 14101776, - "node_id": "MDEyOk9yZ2FuaXphdGlvbjE0MTAxNzc2" - }, - "repo": { - "id": 63260554, - "node_id": "MDEwOlJlcG9zaXRvcnk2MzI2MDU1NA==", - "name": "cocoon", - "full_name": "flutter/cocoon", - "private": false, - "owner": { - "login": "flutter", - "id": 14101776, - "node_id": "MDEyOk9yZ2FuaXphdGlvbjE0MTAxNzc2", - "avatar_url": "https://avatars.githubusercontent.com/u/14101776?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/flutter", - "html_url": "https://github.com/flutter", - "type": "Organization", - "site_admin": false - }, - "default_branch": "main" - } - }, - "author": "ricardoamador", - "auto_merge": null, - "active_lock_reason": null, - "merged": false, - "mergeable": true, - "rebaseable": true, - "mergeable_state": "blocked", - "merged_by": null, - "comments": 1, - "review_comments": 0, - "maintainer_can_modify": false, - "commits": 1, - "additions": 325, - "deletions": 472, - "changed_files": 25 -} -'''; - -const String revertPullRequestFilesJson = ''' - [ - { - "filename": "dashboard/analysis_options.yaml", - "additions": 0, - "deletions": 1, - "changes": 1 - }, - { - "filename": "dashboard/lib/build_dashboard_page.dart", - "additions": 35, - "deletions": 45, - "changes": 80 - }, - { - "filename": "dashboard/lib/index_page.dart", - "additions": 10, - "deletions": 12, - "changes": 22 - }, - { - "filename": "dashboard/lib/logic/brooks.dart", - "additions": 18, - "deletions": 20, - "changes": 38 - }, - { - "filename": "dashboard/lib/logic/links.dart", - "additions": 15, - "deletions": 18, - "changes": 33 - }, - { - "filename": "dashboard/lib/logic/task_grid_filter.dart", - "additions": 4, - "deletions": 12, - "changes": 16 - }, - { - "filename": "dashboard/lib/main.dart", - "additions": 6, - "deletions": 7, - "changes": 13 - }, - { - "filename": "dashboard/lib/service/appengine_cocoon.dart", - "additions": 16, - "deletions": 23, - "changes": 39 - }, - { - "filename": "dashboard/lib/service/dev_cocoon.dart", - "additions": 8, - "deletions": 15, - "changes": 23 - }, - { - "filename": "dashboard/lib/widgets/lattice.dart", - "additions": 18, - "deletions": 22, - "changes": 40 - }, - { - "filename": "dashboard/lib/widgets/luci_task_attempt_summary.dart", - "additions": 4, - "deletions": 5, - "changes": 9 - }, - { - "filename": "dashboard/lib/widgets/sign_in_button.dart", - "additions": 10, - "deletions": 12, - "changes": 22 - }, - { - "filename": "dashboard/lib/widgets/task_grid.dart", - "additions": 13, - "deletions": 17, - "changes": 30 - }, - { - "filename": "dashboard/lib/widgets/task_overlay.dart", - "additions": 2, - "deletions": 3, - "changes": 5 - }, - { - "filename": "dashboard/pubspec.lock", - "additions": 1, - "deletions": 1, - "changes": 2 - }, - { - "filename": "dashboard/test/build_dashboard_page_test.dart", - "additions": 21, - "deletions": 33, - "changes": 54 - }, - { - "filename": "dashboard/test/index_page_test.dart", - "additions": 4, - "deletions": 7, - "changes": 11 - }, - { - "filename": "dashboard/test/logic/qualified_task_test.dart", - "additions": 9, - "deletions": 16, - "changes": 25 - }, - { - "filename": "dashboard/test/logic/task_grid_filter_test.dart", - "additions": 11, - "deletions": 18, - "changes": 29 - }, - { - "filename": "dashboard/test/service/appengine_cocoon_test.dart", - "additions": 74, - "deletions": 112, - "changes": 186 - }, - { - "filename": "dashboard/test/service/google_authentication_test.dart", - "additions": 2, - "deletions": 4, - "changes": 6 - }, - { - "filename": "dashboard/test/state/build_test.dart", - "additions": 20, - "deletions": 31, - "changes": 51 - }, - { - "filename": "dashboard/test/utils/fake_build.dart", - "additions": 6, - "deletions": 10, - "changes": 16 - }, - { - "filename": "dashboard/test/utils/golden.dart", - "additions": 4, - "deletions": 6, - "changes": 10 - }, - { - "filename": "dashboard/test/widgets/accessibility_test.dart", - "additions": 14, - "deletions": 22, - "changes": 36 - } - ] -'''; - -const String originalPullRequestJson = ''' - { - "url": "https://api.github.com/repos/flutter/cocoon/pulls/2024", - "id": 1012225049, - "node_id": "PR_kwDOA8VHis48VVQZ", - "number": 2024, - "state": "closed", - "locked": false, - "title": "Trailing comma analysis", - "user": { - "login": "ricardoamador", - "id": 32242716 - }, - "body": "## Description Add trailing comma requirement to analysis file. * if you had to change anything in the [flutter/tests] repo, include a link to the migration guide as per the [breaking change policy].* ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read the [Flutter Style Guide] _recently_, and have followed its advice. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. [Contributor Guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests [Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat", - "created_at": "2022-07-29T17:56:31Z", - "labels": [ - { - "id": 3960015931, - "node_id": "LA_kwDOA8VHis7sCQw7", - "url": "https://api.github.com/repos/flutter/cocoon/labels/autosubmit", - "name": "autosubmit", - "color": "0E8A16", - "default": false, - "description": "Merge PR when tree becomes green via auto submit App" - } - ], - "head": { - "label": "ricardoamador:trailing_comma_analysis", - "ref": "trailing_comma_analysis", - "sha": "8f2f3d7157345fc1d1c497e1b8106a1d9716fac8", - "user": { - "login": "ricardoamador", - "id": 32242716, - "node_id": "MDQ6VXNlcjMyMjQyNzE2" - }, - "repo": { - "id": 494596620, - "node_id": "R_kgDOHXryDA", - "name": "cocoon", - "full_name": "ricardoamador/cocoon", - "private": false, - "owner": { - "login": "ricardoamador", - "id": 32242716, - "node_id": "MDQ6VXNlcjMyMjQyNzE2", - "avatar_url": "https://avatars.githubusercontent.com/u/32242716?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/ricardoamador", - "html_url": "https://github.com/ricardoamador", - "type": "User", - "site_admin": false - }, - "default_branch": "main" - } - }, - "base": { - "label": "flutter:main", - "ref": "main", - "sha": "bb71fb95db4b1ac61194cd2a12134e9a469960a9", - "user": { - "login": "flutter", - "id": 14101776, - "node_id": "MDEyOk9yZ2FuaXphdGlvbjE0MTAxNzc2" - }, - "repo": { - "id": 63260554, - "node_id": "MDEwOlJlcG9zaXRvcnk2MzI2MDU1NA==", - "name": "cocoon", - "full_name": "flutter/cocoon", - "private": false, - "owner": { - "login": "flutter", - "id": 14101776, - "node_id": "MDEyOk9yZ2FuaXphdGlvbjE0MTAxNzc2", - "avatar_url": "https://avatars.githubusercontent.com/u/14101776?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/flutter", - "html_url": "https://github.com/flutter", - "type": "Organization", - "site_admin": false - }, - "default_branch": "main" - } - }, - "auto_merge": null, - "active_lock_reason": null, - "merged": true, - "mergeable": null, - "rebaseable": null, - "mergeable_state": "unknown", - "merged_by": { - "login": "auto-submit[bot]", - "id": 98614782, - "node_id": "BOT_kgDOBeC9_g", - "avatar_url": "https://avatars.githubusercontent.com/u/14101776?v=4", - "gravatar_id": "", - "url": "https://api.github.com/users/auto-submit%5Bbot%5D", - "html_url": "https://github.com/apps/auto-submit", - "type": "Bot", - "site_admin": false - }, - "comments": 0, - "review_comments": 0, - "maintainer_can_modify": false, - "commits": 4, - "additions": 472, - "deletions": 325, - "changed_files": 25 -} -'''; - -const String originalPullRequestFilesJson = ''' - [ - { - "filename": "dashboard/analysis_options.yaml", - "additions": 1, - "deletions": 0, - "changes": 1 - }, - { - "filename": "dashboard/lib/build_dashboard_page.dart", - "additions": 45, - "deletions": 35, - "changes": 80 - }, - { - "filename": "dashboard/lib/index_page.dart", - "additions": 12, - "deletions": 10, - "changes": 22 - }, - { - "filename": "dashboard/lib/logic/brooks.dart", - "additions": 20, - "deletions": 18, - "changes": 38 - }, - { - "filename": "dashboard/lib/logic/links.dart", - "additions": 18, - "deletions": 15, - "changes": 33 - }, - { - "filename": "dashboard/lib/logic/task_grid_filter.dart", - "additions": 12, - "deletions": 4, - "changes": 16 - }, - { - "filename": "dashboard/lib/main.dart", - "additions": 7, - "deletions": 6, - "changes": 13 - }, - { - "filename": "dashboard/lib/service/appengine_cocoon.dart", - "additions": 23, - "deletions": 16, - "changes": 39 - }, - { - "filename": "dashboard/lib/service/dev_cocoon.dart", - "additions": 15, - "deletions": 8, - "changes": 23 - }, - { - "filename": "dashboard/lib/widgets/lattice.dart", - "additions": 22, - "deletions": 18, - "changes": 40 - }, - { - "filename": "dashboard/lib/widgets/luci_task_attempt_summary.dart", - "additions": 5, - "deletions": 4, - "changes": 9 - }, - { - "filename": "dashboard/lib/widgets/sign_in_button.dart", - "additions": 12, - "deletions": 10, - "changes": 22 - }, - { - "filename": "dashboard/lib/widgets/task_grid.dart", - "additions": 17, - "deletions": 13, - "changes": 30 - }, - { - "filename": "dashboard/lib/widgets/task_overlay.dart", - "additions": 3, - "deletions": 2, - "changes": 5 - }, - { - "filename": "dashboard/pubspec.lock", - "additions": 1, - "deletions": 1, - "changes": 2 - }, - { - "filename": "dashboard/test/build_dashboard_page_test.dart", - "additions": 33, - "deletions": 21, - "changes": 54 - }, - { - "filename": "dashboard/test/index_page_test.dart", - "additions": 7, - "deletions": 4, - "changes": 11 - }, - { - "filename": "dashboard/test/logic/qualified_task_test.dart", - "additions": 16, - "deletions": 9, - "changes": 25 - }, - { - "filename": "dashboard/test/logic/task_grid_filter_test.dart", - "additions": 18, - "deletions": 11, - "changes": 29 - }, - { - "filename": "dashboard/test/service/appengine_cocoon_test.dart", - "additions": 112, - "deletions": 74, - "changes": 186 - }, - { - "filename": "dashboard/test/service/google_authentication_test.dart", - "additions": 4, - "deletions": 2, - "changes": 6 - }, - { - "filename": "dashboard/test/state/build_test.dart", - "additions": 31, - "deletions": 20, - "changes": 51 - }, - { - "filename": "dashboard/test/utils/fake_build.dart", - "additions": 10, - "deletions": 6, - "changes": 16 - }, - { - "filename": "dashboard/test/utils/golden.dart", - "additions": 6, - "deletions": 4, - "changes": 10 - }, - { - "filename": "dashboard/test/widgets/accessibility_test.dart", - "additions": 22, - "deletions": 14, - "changes": 36 - } - ] -'''; - -const String originalPullRequestFilesSubsetJson = ''' - [ - { - "filename": "dashboard/analysis_options.yaml", - "additions": 1, - "deletions": 0, - "changes": 1 - }, - { - "filename": "dashboard/lib/build_dashboard_page.dart", - "additions": 45, - "deletions": 35, - "changes": 80 - }, - { - "filename": "dashboard/lib/index_page.dart", - "additions": 12, - "deletions": 10, - "changes": 22 - }, - { - "filename": "dashboard/lib/logic/brooks.dart", - "additions": 20, - "deletions": 18, - "changes": 38 - }, - { - "filename": "dashboard/lib/logic/links.dart", - "additions": 18, - "deletions": 15, - "changes": 33 - }, - { - "filename": "dashboard/lib/logic/task_grid_filter.dart", - "additions": 12, - "deletions": 4, - "changes": 16 - }, - { - "filename": "dashboard/lib/service/appengine_cocoon.dart", - "additions": 23, - "deletions": 16, - "changes": 39 - }, - { - "filename": "dashboard/lib/service/dev_cocoon.dart", - "additions": 15, - "deletions": 8, - "changes": 23 - }, - { - "filename": "dashboard/lib/widgets/lattice.dart", - "additions": 22, - "deletions": 18, - "changes": 40 - }, - { - "filename": "dashboard/lib/widgets/sign_in_button.dart", - "additions": 12, - "deletions": 10, - "changes": 22 - }, - { - "filename": "dashboard/lib/widgets/task_grid.dart", - "additions": 17, - "deletions": 13, - "changes": 30 - }, - { - "filename": "dashboard/lib/widgets/task_overlay.dart", - "additions": 3, - "deletions": 2, - "changes": 5 - }, - { - "filename": "dashboard/pubspec.lock", - "additions": 1, - "deletions": 1, - "changes": 2 - }, - { - "filename": "dashboard/test/build_dashboard_page_test.dart", - "additions": 33, - "deletions": 21, - "changes": 54 - }, - { - "filename": "dashboard/test/index_page_test.dart", - "additions": 7, - "deletions": 4, - "changes": 11 - }, - { - "filename": "dashboard/test/logic/qualified_task_test.dart", - "additions": 16, - "deletions": 9, - "changes": 25 - }, - { - "filename": "dashboard/test/logic/task_grid_filter_test.dart", - "additions": 18, - "deletions": 11, - "changes": 29 - }, - { - "filename": "dashboard/test/service/appengine_cocoon_test.dart", - "additions": 112, - "deletions": 74, - "changes": 186 - }, - { - "filename": "dashboard/test/service/google_authentication_test.dart", - "additions": 4, - "deletions": 2, - "changes": 6 - }, - { - "filename": "dashboard/test/state/build_test.dart", - "additions": 31, - "deletions": 20, - "changes": 51 - }, - { - "filename": "dashboard/test/utils/fake_build.dart", - "additions": 10, - "deletions": 6, - "changes": 16 - }, - { - "filename": "dashboard/test/utils/golden.dart", - "additions": 6, - "deletions": 4, - "changes": 10 - }, - { - "filename": "dashboard/test/widgets/accessibility_test.dart", - "additions": 22, - "deletions": 14, - "changes": 36 - } - ] -'''; - -const String ciyamlCheckRun = ''' -{ - "total_count": 1, - "check_runs": [ - { - "id": 8752872923, - "name": "ci.yaml validation", - "head_sha": "60612a38d705d00a234e0aabba08247fc0dda1ac", - "status": "completed", - "conclusion": "success", - "started_at": "2022-10-06T20:50:57Z", - "completed_at": "2022-10-06T20:51:40Z", - "check_suite": { - "id": 8654966141 - } - } - ] -} -'''; - -const String ciyamlCheckRunNotComplete = ''' -{ - "total_count": 1, - "check_runs": [ - { - "id": 8752872923, - "name": "ci.yaml validation", - "head_sha": "60612a38d705d00a234e0aabba08247fc0dda1ac", - "status": "in_progress", - "started_at": "2022-10-06T20:50:57Z", - "completed_at": "2022-10-06T20:51:40Z", - "check_suite": { - "id": 8654966141 - } - } - ] -} -'''; diff --git a/cipd_packages/README.md b/cipd_packages/README.md deleted file mode 100644 index 9596db035..000000000 --- a/cipd_packages/README.md +++ /dev/null @@ -1,77 +0,0 @@ -# Flutter CIPD Packages - -This folder contains auto built/deployed public [CIPD packages](https://chrome-infra-packages.appspot.com/p/flutter) -used by Flutter CI. These packages will be consumed from tests and auto cached to boost future runs. - -Please follow these steps to add a new package. - -## Creating the package build file - -Create a new sub directory under this folder named with the new package. Add a `build.sh` script for Linux/Mac or -`build.bat` for Windows, and put it under a child dir `tool`. This is the main script to build this package. Add -necessary lib files if needed. - -Note: - -1) we do not archive the binaries in the repo, and will auto build package artifacts and upload to CIPD via the bot. -2) please add a code owner entry under section `cipd packages` in [CODEOWNERS](https://github.com/flutter/cocoon/blob/main/CODEOWNERS) file. - -## Add an entry to cocoon .ci.yaml - -This is to enable the auto build and upload. Please follow the following example format: -```yaml - - name: Mac - recipe: cocoon/cipd - bringup: true - properties: - script: cipd_packages//tool/build.sh - cipd_name: flutter//mac-amd64 # Use mac-arm64 for arm64 version. - device_type: none - runIf: - - cipd_packages//** - - .ci.yaml -``` - -Start with `bringup: true`, same as any other regular new target. Once validated in CI, remove it to enable running -in the `prod` environment so that artifacts are to be uploaded to CIPD. - -Note: with `bringup: true`, the target will be executed in a staging environment and it validates only the logic -and will not upload to CIPD. - -## Adding a reference to the CIPD package - -Until this step, artifacts are being uploaded to CIPD whenever a new commit is merged. It is useful to add a reference -to the package, so that we can use the reference in the CI recipe. This way we won’t need to change the recipe -whenever we update the package. - -Googlers have default access to add a reference to a package via: -```sh -cipd set-ref flutter/PackageName/mac-amd64 -ref Reference -version InstanceID -``` - -* Reference: e.g. major release versions. If not specified, `latest` will be used based on the latest package instance. -* InstaneID: this can be obtained from the package page, e.g. [ruby](https://chrome-infra-packages.appspot.com/p/flutter/ruby/mac-amd64/+/TyvPskvefNRkTDmiDcwRHrdL_a2FQE_4wBojOqhxdtYC). - -Note: for non-Googler contributors, please file an [infra bug](https://github.com/flutter/flutter/issues/new?assignees=&labels=team-infra&projects=&template=6_infrastructure.yml) to make a reference request. - -## Supporting packages download from CI recipe - -Example CL: [51547 ](https://flutter-review.googlesource.com/c/recipes/+/51547) - -Refer to [CONTRIBUTING.md](https://flutter.googlesource.com/recipes/+/refs/heads/main/CONTRIBUTING.md) on how to -contribute to recipes repository. - -## Adding/updating package dependency from .ci.yaml from different repositories - -This is the last step to enable the package usage in the real CI. Add or update the new package to either the platform level or -target level entries for your targeted repository: -``` yaml -dependencies: >- - [ - {"dependency": "chrome_and_driver", "version": "Reference"}, - ] -``` - -Note: use the `Reference` created above. - -More details about configuration setup can be found in [CI_YAML.md](https://github.com/flutter/cocoon/blob/main/CI_YAML.md). \ No newline at end of file diff --git a/cipd_packages/codesign/.gitignore b/cipd_packages/codesign/.gitignore deleted file mode 100644 index 3c8a15727..000000000 --- a/cipd_packages/codesign/.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/cipd_packages/codesign/LICENSE b/cipd_packages/codesign/LICENSE deleted file mode 100644 index d5384ca42..000000000 --- a/cipd_packages/codesign/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/cipd_packages/codesign/README.md b/cipd_packages/codesign/README.md deleted file mode 100644 index ce23050b4..000000000 --- a/cipd_packages/codesign/README.md +++ /dev/null @@ -1,56 +0,0 @@ -# codesign - -A standalone tool to codesign Mac engine binaries. - -## Building - -This tool is meant to be published as an -[AOT compiled binary](https://chrome-infra-packages.appspot.com/p/flutter/codesign) -distributed via CIPD. - -Build the tool for different host platforms on corresponding machines. It will -automatically download a suitable version of Dart to build the binary. - -To create the CIPD package, make sure that the `build/` folder does not exist. - -### Auto build - -Every new commit will trigger pre-submit builders to auto build a new version -for different platforms without any tag/ref. - -When a new commit is submitted, post-submit builders will trigger the build of -a new version of the cipd package, and tag the package with `latest`. - -### Manual build - -Running `tool/build.sh` will build an executable binary in -the `build` folder. Then push to cipd by running - -```bash -cipd create -in build \ - -name flutter/codesign/-amd64 \ - -ref \ - -tag sha_timestamp:_ -``` - -* os: `linux`, `mac`, or `windows`. -* ref: `release` or `staging` - -## How to use - -`codesign` is the executable binary in the `build` folder, and can be called via - - ```bash - ./codesign --[no-]dryrun - --codesign-cert-name="FLUTTER.IO LLC" - --codesign-team-id-file-path=/a/b/c.txt - --codesign-appstore-id-file-path=/a/b/b.txt - --app-specific-password-file-path=/a/b/a.txt - --input-zip-file-path=/a/input.zip - --output-zip-file-path=/b/output.zip - ``` - -Use `codesign --help` to learn more. - -Alternatively, if user has dart installed and does not wish to build a binary, -codesign app can be invoked via `dart run bin/codesign.dart --`. diff --git a/cipd_packages/codesign/analysis_options.yaml b/cipd_packages/codesign/analysis_options.yaml deleted file mode 100644 index f04c6cf0f..000000000 --- a/cipd_packages/codesign/analysis_options.yaml +++ /dev/null @@ -1 +0,0 @@ -include: ../../analysis_options.yaml diff --git a/cipd_packages/codesign/bin/codesign.dart b/cipd_packages/codesign/bin/codesign.dart deleted file mode 100644 index b5a73e946..000000000 --- a/cipd_packages/codesign/bin/codesign.dart +++ /dev/null @@ -1,158 +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 'package:args/args.dart'; -import 'package:codesign/codesign.dart'; -import 'package:file/file.dart'; -import 'package:file/local.dart'; -import 'package:logging/logging.dart'; -import 'package:platform/platform.dart'; -import 'package:process/process.dart'; - -/// Definitions of variables are included in help texts below. -const String kHelpFlag = 'help'; -const String kDryrunFlag = 'dryrun'; -const String kCodesignCertNameOption = 'codesign-cert-name'; -const String kInputZipPathOption = 'input-zip-file-path'; -const String kOutputZipPathOption = 'output-zip-file-path'; -const String kAppSpecificPasswordOption = 'app-specific-password-file-path'; -const String kCodesignAppstoreIDOption = 'codesign-appstore-id-file-path'; -const String kCodesignTeamIDOption = 'codesign-team-id-file-path'; - -/// Perform Mac code signing based on file paths. -/// -/// By default, if a user does not specify a dryrun flag, or selects dryrun -/// mode by providing the `--dryrun` flag, then [kDryrunFlag] is set to true, -/// a quick sanity check is performed and the notarization process is skipped. -/// On the other hand, if a user provides the flag `--no-dryrun`, [kDryrunFlag] -/// will be set to false, and code signed artifacts will go through the notarization -/// process. -/// -/// For [kInputZipPathOption] and [kOutputZipPathOption], they are required parameter to specify the -/// input and output locations. -/// The codesign app will take the zip file located at the input location [kInputZipPathOption], and -/// put codesigned zip at [kOutputZipPathOption]. The work of downloading and uploading the zip -/// artifacts is delegated to recipe. -/// For example, supply -/// '--input-zip-file-path=/tmp/input.zip', -/// and code sign app will code sign the artifacts located at /tmp/input.zip. -/// -/// For [kAppSpecificPasswordOption], [kCodesignAppstoreIDOption] and [kCodesignTeamIDOption], -/// they are file paths of the password files in the file system. -/// Each of the file paths stores a single line of sensitive password. -/// sensitive passwords include , , and . -/// For example, if a user supplies --app-specific-password-file-path=/tmp/passwords.txt, -/// then we would be expecting a password file located at /tmp/passwords.txt. -/// The password file should contain the password name APP-SPECIFIC-PASSWORD and its value, deliminated by a single colon. -/// The content of a password file would look similar to: -/// APP-SPECIFIC-PASSWORD:789 -/// -/// [kCodesignCertNameOption] is public information. For codesigning flutter artifacts, -/// a user can provide values for this variable as shown in the example below. -/// -/// Note: this tool uses Apple developer identity from keychain named build.keychain. -/// You can create/delete build.keychain by following the steps below: -/// /usr/bin/security create-keychain -p '' build.keychain # for create -/// /usr/bin/security delete-keychain build.keychain # for delete -/// -/// Usage: -/// ```shell -/// dart run bin/codesign.dart --[no-]dryrun -/// --codesign-cert-name="FLUTTER.IO LLC" -/// --codesign-team-id-file-path=/a/b/c.txt -/// --codesign-appstore-id-file-path=/a/b/b.txt -/// --app-specific-password-file-path=/a/b/a.txt -/// --input-zip-file-path=/a/input.zip -/// --output-zip-file-path=/b/output.zip -/// ``` -Future main(List args) async { - Logger.root.onRecord.listen((LogRecord record) { - stdout.writeln(record.toString()); - }); - - final ArgParser parser = ArgParser(); - parser - ..addFlag( - kHelpFlag, - help: 'Prints usage info.', - callback: (bool value) { - if (value) { - stdout.write('${parser.usage}\n'); - exit(1); - } - }, - ) - ..addOption( - kCodesignCertNameOption, - help: 'The name of the codesign certificate to be used when codesigning.' - 'the name of the certificate for flutter, for example, is: FLUTTER.IO LLC', - ) - ..addOption( - kInputZipPathOption, - help: 'File path to the unsigned artifact zip file.', - ) - ..addOption( - kOutputZipPathOption, - help: 'File path to codesigned artifact zip file for output.', - ) - ..addOption( - kAppSpecificPasswordOption, - help: - 'The file path of a password file in file system. The password file stores the sensitive password .', - ) - ..addOption( - kCodesignAppstoreIDOption, - help: - 'The file path of a password file in file system. The password file stores the sensitive password .', - ) - ..addOption( - kCodesignTeamIDOption, - help: - 'The file path of a password file in file system. The password file stores the sensitive password .', - ) - ..addFlag( - kDryrunFlag, - defaultsTo: true, - help: 'whether we are going to skip the notarization process.', - ); - - final ArgResults argResults = parser.parse(args); - - const Platform platform = LocalPlatform(); - - final String codesignCertName = getValueFromArgs(kCodesignCertNameOption, argResults)!; - final String inputZipPath = getValueFromArgs(kInputZipPathOption, argResults)!; - final String outputZipPath = getValueFromArgs(kOutputZipPathOption, argResults)!; - final String appSpecificPasswordFilePath = getValueFromArgs(kAppSpecificPasswordOption, argResults)!; - final String codesignAppstoreIDFilePath = getValueFromArgs(kCodesignAppstoreIDOption, argResults)!; - final String codesignTeamIDFilePath = getValueFromArgs(kCodesignTeamIDOption, argResults)!; - - final bool dryrun = argResults[kDryrunFlag] as bool; - - if (!platform.isMacOS) { - throw CodesignException( - 'Error! Expected operating system "macos", actual operating system is: ' - '"${platform.operatingSystem}"', - ); - } - - const FileSystem fileSystem = LocalFileSystem(); - final Directory rootDirectory = fileSystem.systemTempDirectory.createTempSync('conductor_codesign'); - const ProcessManager processManager = LocalProcessManager(); - - return FileCodesignVisitor( - codesignCertName: codesignCertName, - fileSystem: fileSystem, - rootDirectory: rootDirectory, - appSpecificPasswordFilePath: appSpecificPasswordFilePath, - codesignAppstoreIDFilePath: codesignAppstoreIDFilePath, - codesignTeamIDFilePath: codesignTeamIDFilePath, - processManager: processManager, - dryrun: dryrun, - inputZipPath: inputZipPath, - outputZipPath: outputZipPath, - ).validateAll(); -} diff --git a/cipd_packages/codesign/bin/verify.dart b/cipd_packages/codesign/bin/verify.dart deleted file mode 100644 index f467d7aa3..000000000 --- a/cipd_packages/codesign/bin/verify.dart +++ /dev/null @@ -1,29 +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' as io; - -import 'package:codesign/verify.dart'; -import 'package:logging/logging.dart'; - -Future main(List args) async { - final Logger logger = Logger('root'); - logger.onRecord.listen((LogRecord record) { - io.stdout.writeln(record.message); - }); - if (args.length != 1) { - logger.info('Usage: dart verify.dart [FILE]'); - io.exit(1); - } - if (!io.Platform.isMacOS) { - logger.severe('This tool must be run from macOS.'); - io.exit(1); - } - final inputFile = args[0]; - final VerificationResult result = await VerificationService( - binaryPath: inputFile, - logger: logger, - ).run(); - io.exit(result == VerificationResult.codesignedAndNotarized ? 0 : 1); -} diff --git a/cipd_packages/codesign/lib/codesign.dart b/cipd_packages/codesign/lib/codesign.dart deleted file mode 100644 index 54fe0c8d3..000000000 --- a/cipd_packages/codesign/lib/codesign.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/file_codesign_visitor.dart'; -export 'src/utils.dart'; diff --git a/cipd_packages/codesign/lib/src/file_codesign_visitor.dart b/cipd_packages/codesign/lib/src/file_codesign_visitor.dart deleted file mode 100644 index 6d5da9c3d..000000000 --- a/cipd_packages/codesign/lib/src/file_codesign_visitor.dart +++ /dev/null @@ -1,499 +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' as io; - -import 'package:file/file.dart'; -import 'package:process/process.dart'; - -import 'log.dart'; -import 'utils.dart'; - -/// Statuses reported by Apple's Notary Server. -/// -/// See more: -/// * https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution/customizing_the_notarization_workflow -enum NotaryStatus { - pending, - failed, - succeeded, -} - -/// Codesign and notarize all files within a [RemoteArchive]. -class FileCodesignVisitor { - FileCodesignVisitor({ - required this.codesignCertName, - required this.fileSystem, - required this.rootDirectory, - required this.processManager, - required this.inputZipPath, - required this.outputZipPath, - required this.appSpecificPasswordFilePath, - required this.codesignAppstoreIDFilePath, - required this.codesignTeamIDFilePath, - this.dryrun = true, - this.notarizationTimerDuration = const Duration(seconds: 5), - }) { - entitlementsFile = rootDirectory.childFile('Entitlements.plist')..writeAsStringSync(_entitlementsFileContents); - } - - /// Temp [Directory] to download/extract files to. - /// - /// This file will be deleted if [validateAll] completes successfully. - final Directory rootDirectory; - final FileSystem fileSystem; - final ProcessManager processManager; - - final String codesignCertName; - final String inputZipPath; - final String outputZipPath; - final String appSpecificPasswordFilePath; - final String codesignAppstoreIDFilePath; - final String codesignTeamIDFilePath; - final bool dryrun; - final Duration notarizationTimerDuration; - - // 'Apple developer account email used for authentication with notary service.' - late String codesignAppstoreId; - // Unique password of the apple developer account.' - late String appSpecificPassword; - // Team-id is used by notary service for xcode version 13+. - late String codesignTeamId; - - Set fileWithEntitlements = {}; - Set fileWithoutEntitlements = {}; - Set fileConsumed = {}; - Set directoriesVisited = {}; - Map availablePasswords = { - 'CODESIGN_APPSTORE_ID': '', - 'CODESIGN_TEAM_ID': '', - 'APP_SPECIFIC_PASSWORD': '', - }; - - late final File entitlementsFile; - - int _remoteDownloadIndex = 0; - int get remoteDownloadIndex => _remoteDownloadIndex++; - - static const String _entitlementsFileContents = ''' - - - - - com.apple.security.cs.allow-jit - - com.apple.security.cs.allow-unsigned-executable-memory - - com.apple.security.cs.allow-dyld-environment-variables - - com.apple.security.network.client - - com.apple.security.network.server - - com.apple.security.cs.disable-library-validation - - - -'''; - static final RegExp _notarytoolStatusCheckPattern = RegExp(r'[ ]*status: ([a-zA-z ]+)'); - static final RegExp _notarytoolRequestPattern = RegExp(r'id: ([a-z0-9-]+)'); - - static const String fixItInstructions = ''' -Codesign test failed. - -We compared binary files in engine artifacts with those listed in -entitlement.txt and withoutEntitlements.txt, and the binary files do not match. -*entitlements.txt is the configuration file encoded in engine artifact zip, -built by BUILD.gn and Ninja, to detail the list of entitlement files. -Either an expected file was not found in *entitlements.txt, or an unexpected -file was found in entitlements.txt. - -This usually happens during an engine roll. -If this is a valid change, then BUILD.gn needs to be changed. -Binaries that will run on a macOS host require entitlements, and -binaries that run on an iOS device must NOT have entitlements. -For example, if this is a new binary that runs on macOS host, add it -to [entitlements.txt] file inside the zip artifact produced by BUILD.gn. -If this is a new binary that needs to be run on iOS device, add it -to [withoutEntitlements.txt]. -If there are obsolete binaries in entitlements configuration files, please delete or -update these file paths accordingly. -'''; - - /// Read a single line of password stored at [passwordFilePath]. - Future readPassword(String passwordFilePath) async { - if (!(await fileSystem.file(passwordFilePath).exists())) { - throw CodesignException('$passwordFilePath not found \n' - 'make sure you have provided codesign credentials in a file \n'); - } - return fileSystem.file(passwordFilePath).readAsString(); - } - - /// The entrance point of examining and code signing an engine artifact. - Future validateAll() async { - codesignAppstoreId = await readPassword(codesignAppstoreIDFilePath); - codesignTeamId = await readPassword(codesignTeamIDFilePath); - appSpecificPassword = await readPassword(appSpecificPasswordFilePath); - - await processRemoteZip(); - - log.info('Codesign completed. Codesigned zip is located at $outputZipPath.' - 'If you have uploaded the artifacts back to google cloud storage, please delete' - ' the folder $outputZipPath and $inputZipPath.'); - if (dryrun) { - log.info('code signing dry run has completed, this is a quick sanity check without' - 'going through the notary service. To run the full codesign process, use --no-dryrun flag.'); - } - } - - /// Process engine artifacts from [inputZipPath] and kick start a - /// recursive visit of its contents. - /// - /// Invokes [visitDirectory] to recursively visit the contents of the remote - /// zip. Notarizes the engine artifact if [dryrun] is false. - /// Returns null as result if [dryrun] is true. - Future processRemoteZip() async { - // download the zip file - final File originalFile = rootDirectory.fileSystem.file(inputZipPath); - - // This is the starting directory of the unzipped artifact. - final Directory parentDirectory = rootDirectory.childDirectory('single_artifact'); - - await unzip( - inputZip: originalFile, - outDir: parentDirectory, - processManager: processManager, - ); - - //extract entitlements file. - fileWithEntitlements = await parseEntitlements(parentDirectory, true); - fileWithoutEntitlements = await parseEntitlements(parentDirectory, false); - log.info('parsed binaries with entitlements are $fileWithEntitlements'); - log.info('parsed binaries without entitlements are $fileWithEntitlements'); - - // recursively visit extracted files - await visitDirectory(directory: parentDirectory, parentVirtualPath: ''); - - await zip( - inputDir: parentDirectory, - outputZipPath: outputZipPath, - processManager: processManager, - ); - - await parentDirectory.delete(recursive: true); - - // `dryrun` flag defaults to true to save time for a faster sanity check - if (!dryrun) { - await notarize(fileSystem.file(outputZipPath)); - - return outputZipPath; - } - return null; - } - - /// Visit a [Directory] type while examining the file system extracted from an artifact. - Future visitDirectory({ - required Directory directory, - required String parentVirtualPath, - }) async { - log.info('Visiting directory ${directory.absolute.path}'); - if (directoriesVisited.contains(directory.absolute.path)) { - log.warning( - 'Warning! You are visiting a directory that has been visited before, the directory is ${directory.absolute.path}', - ); - } - directoriesVisited.add(directory.absolute.path); - - await cleanupEntitlements(directory); - - final List entities = await directory.list(followLinks: false).toList(); - for (FileSystemEntity entity in entities) { - if (entity is io.Link) { - log.info('current file or direcotry ${entity.path} is a symlink to ${(entity as io.Link).targetSync()}, ' - 'codesign is therefore skipped for the current file or directory.'); - continue; - } - if (entity is io.Directory) { - await visitDirectory( - directory: directory.childDirectory(entity.basename), - parentVirtualPath: joinEntitlementPaths(parentVirtualPath, entity.basename), - ); - continue; - } - if (entity.basename == 'entitlements.txt' || entity.basename == 'without_entitlements.txt') { - continue; - } - final FileType childType = getFileType( - entity.absolute.path, - processManager, - ); - if (childType == FileType.zip) { - await visitEmbeddedZip( - zipEntity: entity, - parentVirtualPath: parentVirtualPath, - ); - } else if (childType == FileType.binary) { - await visitBinaryFile(binaryFile: entity as File, parentVirtualPath: parentVirtualPath); - } - log.info('Child file of directory ${directory.basename} is ${entity.basename}'); - } - } - - /// Unzip an [EmbeddedZip] and visit its children. - Future visitEmbeddedZip({ - required FileSystemEntity zipEntity, - required String parentVirtualPath, - }) async { - log.info('This embedded file is ${zipEntity.path} and parentVirtualPath is $parentVirtualPath'); - final String currentFileName = zipEntity.basename; - final Directory newDir = rootDirectory.childDirectory('embedded_zip_${zipEntity.absolute.path.hashCode}'); - await unzip( - inputZip: zipEntity, - outDir: newDir, - processManager: processManager, - ); - - // the virtual file path is advanced by the name of the embedded zip - final String currentZipEntitlementPath = joinEntitlementPaths(parentVirtualPath, currentFileName); - await visitDirectory( - directory: newDir, - parentVirtualPath: currentZipEntitlementPath, - ); - await zipEntity.delete(); - await zip( - inputDir: newDir, - outputZipPath: zipEntity.absolute.path, - processManager: processManager, - ); - await newDir.delete(recursive: true); - } - - /// Visit and codesign a binary with / without entitlement. - /// - /// At this stage, the virtual [entitlementCurrentPath] accumulated through the recursive visit, is compared - /// against the paths extracted from [fileWithEntitlements], to help determine if this file should be signed - /// with entitlements. - Future visitBinaryFile({ - required File binaryFile, - required String parentVirtualPath, - int retryCount = 3, - int sleepTime = 1, - }) async { - final String currentFileName = binaryFile.basename; - final String entitlementCurrentPath = joinEntitlementPaths(parentVirtualPath, currentFileName); - - if (!fileWithEntitlements.contains(entitlementCurrentPath) && - !fileWithoutEntitlements.contains(entitlementCurrentPath)) { - log.severe('the binary file $currentFileName is causing an issue. \n' - 'This file is located at $entitlementCurrentPath in the flutter engine artifact.'); - log.severe('The system has detected a binary file at $entitlementCurrentPath. ' - 'But it is not in the entitlements configuration files you provided. ' - 'If this is a new engine artifact, please add it to one of the entitlements.txt files.'); - throw CodesignException(fixItInstructions); - } - log.info('signing file at path ${binaryFile.absolute.path}'); - log.info('the virtual entitlement path associated with file is $entitlementCurrentPath'); - log.info('the decision to sign with entitlement is ${fileWithEntitlements.contains(entitlementCurrentPath)}'); - fileConsumed.add(entitlementCurrentPath); - if (dryrun) { - return; - } - final List args = [ - '/usr/bin/codesign', - '--keychain', - 'build.keychain', // specify the keychain to look for cert - '-f', // force - '-s', // use the cert provided by next argument - codesignCertName, - binaryFile.absolute.path, - '--timestamp', // add a secure timestamp - '--options=runtime', // hardened runtime - if (fileWithEntitlements.contains(entitlementCurrentPath)) ...[ - '--entitlements', - entitlementsFile.absolute.path, - ], - ]; - - io.ProcessResult? result; - while (retryCount > 0) { - log.info('Executing: ${args.join(' ')}\n'); - result = await processManager.run(args); - if (result.exitCode == 0) { - return; - } - - log.severe( - 'Failed to codesign ${binaryFile.absolute.path} with args: ${args.join(' ')}\n' - 'stdout:\n${(result.stdout as String).trim()}' - 'stderr:\n${(result.stderr as String).trim()}', - ); - - retryCount -= 1; - await Future.delayed(Duration(seconds: sleepTime)); - sleepTime *= 2; - } - throw CodesignException('Failed to codesign ${binaryFile.absolute.path} with args: ${args.join(' ')}\n' - 'stdout:\n${(result!.stdout as String).trim()}\n' - 'stderr:\n${(result.stderr as String).trim()}'); - } - - /// Delete codesign metadata at ALL places inside engine binary. - /// - /// Context: https://github.com/flutter/flutter/issues/126705. This is a temporary workaround. - /// Once flutter tools is ready we can remove this logic. - Future cleanupEntitlements(Directory parent) async { - final String metadataEntitlements = fileSystem.path.join(parent.path, 'entitlements.txt'); - final String metadataWithoutEntitlements = fileSystem.path.join(parent.path, 'without_entitlements.txt'); - for (String metadataPath in [metadataEntitlements, metadataWithoutEntitlements]) { - if (await fileSystem.file(metadataPath).exists()) { - log.warning('cleaning up codesign metadata at $metadataPath.'); - await fileSystem.file(metadataPath).delete(); - } - } - } - - /// Extract entitlements configurations from downloaded zip files. - /// - /// Parse and store codesign configurations detailed in configuration files. - /// File paths of entilement files and non entitlement files will be parsed and stored in [fileWithEntitlements]. - Future> parseEntitlements(Directory parent, bool entitlements) async { - final String entitlementFilePath = entitlements - ? fileSystem.path.join(parent.path, 'entitlements.txt') - : fileSystem.path.join(parent.path, 'without_entitlements.txt'); - if (!(await fileSystem.file(entitlementFilePath).exists())) { - log.warning('$entitlementFilePath not found. ' - 'by default, system will assume there is no ${entitlements ? '' : 'without_'}entitlements file. ' - 'As a result, no binary will be codesigned.' - 'if this is not intended, please provide them along with the engine artifacts.'); - return {}; - } - - final Set fileWithEntitlements = {}; - - fileWithEntitlements.addAll(await fileSystem.file(entitlementFilePath).readAsLines()); - // TODO(xilaizhang) : add back metadata information after https://github.com/flutter/flutter/issues/126705 - // is resolved. - await fileSystem.file(entitlementFilePath).delete(); - - return fileWithEntitlements; - } - - /// Upload a zip archive to the notary service and verify the build succeeded. - /// - /// The apple notarization service will unzip the artifact, validate all - /// binaries are properly codesigned, and notarize the entire archive. - Future notarize(File file) async { - final Completer completer = Completer(); - final String uuid = uploadZipToNotary(file); - - Future callback(Timer timer) async { - final bool notaryFinished = checkNotaryJobFinished(uuid); - if (notaryFinished) { - timer.cancel(); - log.info('successfully notarized ${file.path}'); - completer.complete(); - } - } - - // check on results - Timer.periodic( - notarizationTimerDuration, - callback, - ); - await completer.future; - } - - /// Make a request to the notary service to see if the notary job is finished. - /// - /// A return value of true means that notarization finished successfully, - /// false means that the job is still pending. If the notarization fails, this - /// function will throw a [ConductorException]. - bool checkNotaryJobFinished(String uuid) { - final List args = [ - 'xcrun', - 'notarytool', - 'info', - uuid, - '--password', - appSpecificPassword, - '--apple-id', - codesignAppstoreId, - '--team-id', - codesignTeamId, - ]; - - log.info('checking notary status with ${args.join(' ')}'); - final io.ProcessResult result = processManager.runSync(args); - final String combinedOutput = (result.stdout as String) + (result.stderr as String); - - final RegExpMatch? match = _notarytoolStatusCheckPattern.firstMatch(combinedOutput); - - if (match == null) { - throw CodesignException( - 'Malformed output from "${args.join(' ')}"\n${combinedOutput.trim()}', - ); - } - - final String status = match.group(1)!; - - if (status == 'Accepted') { - return true; - } - if (status == 'In Progress') { - log.info('job $uuid still pending'); - return false; - } - throw CodesignException('Notarization failed with: $status\n$combinedOutput'); - } - - /// Upload artifact to Apple notary service. - String uploadZipToNotary(File localFile, [int retryCount = 3, int sleepTime = 1]) { - while (retryCount > 0) { - final List args = [ - 'xcrun', - 'notarytool', - 'submit', - localFile.absolute.path, - '--apple-id', - codesignAppstoreId, - '--password', - appSpecificPassword, - '--team-id', - codesignTeamId, - ]; - - log.info('uploading ${args.join(' ')}'); - final io.ProcessResult result = processManager.runSync(args); - if (result.exitCode != 0) { - throw CodesignException( - 'Command "${args.join(' ')}" failed with exit code ${result.exitCode}\nStdout: ${result.stdout}\nStderr: ${result.stderr}', - ); - } - - final String combinedOutput = (result.stdout as String) + (result.stderr as String); - final RegExpMatch? match; - match = _notarytoolRequestPattern.firstMatch(combinedOutput); - - if (match == null) { - log.warning('Failed to upload to the notary service with args: ${args.join(' ')}'); - log.warning('{combinedOutput.trim()}'); - retryCount -= 1; - log.warning('Trying again $retryCount more time${retryCount > 1 ? 's' : ''}...'); - io.sleep(Duration(seconds: sleepTime)); - continue; - } - - final String requestUuid = match.group(1)!; - log.info('RequestUUID for ${localFile.path} is: $requestUuid'); - - return requestUuid; - } - log.warning('The upload to notary service failed after retries, and' - ' the output format does not match the current notary tool version.' - ' If after inspecting the output, you believe the process finished ' - 'successfully but was not detected, please contact flutter release engineers'); - throw CodesignException('Failed to upload ${localFile.path} to the notary service'); - } -} diff --git a/cipd_packages/codesign/lib/src/log.dart b/cipd_packages/codesign/lib/src/log.dart deleted file mode 100644 index 6c5e18dd3..000000000 --- a/cipd_packages/codesign/lib/src/log.dart +++ /dev/null @@ -1,7 +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:logging/logging.dart'; - -final Logger log = Logger('codesign'); diff --git a/cipd_packages/codesign/lib/src/utils.dart b/cipd_packages/codesign/lib/src/utils.dart deleted file mode 100644 index 0d30e2984..000000000 --- a/cipd_packages/codesign/lib/src/utils.dart +++ /dev/null @@ -1,117 +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 'package:args/args.dart'; -import 'package:file/file.dart'; -import 'package:process/process.dart'; - -import 'log.dart'; - -enum FileType { - folder, - zip, - binary, - other; - - // Artifact files have many different types. Codesign would be no-op when encoutering non typical types such as application/octet-stream. - factory FileType.fromMimeType(String mimeType) { - if (mimeType.contains('inode/directory')) { - return FileType.folder; - } else if (mimeType.contains('application/zip')) { - return FileType.zip; - } else if (mimeType.contains('application/x-mach-binary')) { - return FileType.binary; - } else { - return FileType.other; - } - } -} - -Future unzip({ - required FileSystemEntity inputZip, - required Directory outDir, - required ProcessManager processManager, -}) async { - await processManager.run( - [ - 'unzip', - inputZip.absolute.path, - '-d', - outDir.absolute.path, - ], - ); - log.info('The downloaded file is unzipped from ${inputZip.absolute.path} to ${outDir.absolute.path}'); -} - -Future zip({ - required Directory inputDir, - required String outputZipPath, - required ProcessManager processManager, -}) async { - await processManager.run( - [ - 'zip', - '--symlinks', - '--recurse-paths', - outputZipPath, - // use '.' so that the full absolute path is not encoded into the zip file - '.', - '--include', - '*', - ], - workingDirectory: inputDir.absolute.path, - ); -} - -/// Check mime-type of file at [filePath] to determine if it is a directory. -FileType getFileType(String filePath, ProcessManager processManager) { - final ProcessResult result = processManager.runSync( - [ - 'file', - '--mime-type', - '-b', // is binary - filePath, - ], - ); - final String output = result.stdout as String; - return FileType.fromMimeType(output); -} - -class CodesignException implements Exception { - CodesignException(this.message); - - final String message; - - @override - String toString() => 'Exception: $message'; -} - -/// Return the command line argument by parsing [argResults]. -/// -/// If the key does not exist in CLI args, throws a [CodesignException]. -String? getValueFromArgs( - String name, - ArgResults argResults, { - bool allowNull = false, -}) { - final String? argValue = argResults[name] as String?; - if (argValue != null) { - return argValue; - } - if (allowNull) { - return null; - } - throw CodesignException('Expected either the CLI arg --$name ' - 'to be provided!'); -} - -String joinEntitlementPaths(String entitlementParentPath, String pathToJoin) { - if (entitlementParentPath == '') { - return pathToJoin; - } else { - return '$entitlementParentPath/$pathToJoin'; - } -} diff --git a/cipd_packages/codesign/lib/verify.dart b/cipd_packages/codesign/lib/verify.dart deleted file mode 100644 index 15c3d2213..000000000 --- a/cipd_packages/codesign/lib/verify.dart +++ /dev/null @@ -1,185 +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' as io; - -import 'package:file/file.dart'; -import 'package:file/local.dart'; -import 'package:logging/logging.dart'; -import 'package:process/process.dart'; - -const String kNotNotarizedMessage = 'test-requirement: code failed to satisfy specified code requirement(s)'; - -enum VerificationResult { - unsigned, - codesignedOnly, - codesignedAndNotarized, -} - -class VerificationService { - VerificationService({ - required this.binaryPath, - required this.logger, - this.fs = const LocalFileSystem(), - this.pm = const LocalProcessManager(), - }) { - if (!fs.file(binaryPath).existsSync()) { - throw Exception( - 'Input file `$binaryPath` does not exist--please provide the path to a ' - 'valid binary to verify.', - ); - } - if (!pm.canRun('codesign')) { - throw Exception( - 'The binary `codesign` is required to run this tool. Do you have ' - 'Xcode installed?', - ); - } - } - - final Logger logger; - final String binaryPath; - final FileSystem fs; - final ProcessManager pm; - - late final String _codesignTimestamp; - late final String _format; - late final String _signatureSize; - late final String _codesignId; - bool? _notarizationStatus; - - Future run() async { - if (!await _codesignDisplay()) { - return VerificationResult.unsigned; - } - await _notarization(); - logger.info(present()); - return _notarizationStatus! ? VerificationResult.codesignedAndNotarized : VerificationResult.codesignedOnly; - } - - Future _notarization() async { - final List command = [ - 'codesign', - '--verify', - '-v', - // force online notarization check - '-R=notarized', - '--check-notarization', - binaryPath, - ]; - final io.ProcessResult result = await pm.run(command); - - // This usually means it does not satisfy notarization requirement - if (result.exitCode == 3 && (result.stderr as String).contains(kNotNotarizedMessage)) { - _notarizationStatus = false; - return; - } - if (result.exitCode != 0) { - throw Exception(''' -Command `${command.join(' ')}` failed with code ${result.exitCode} - -${result.stderr} -'''); - } - final String stderr = result.stderr as String; - if (stderr.contains('explicit requirement satisfied')) { - _notarizationStatus = true; - return; - } - throw UnimplementedError('Failed parsing the output of `${command.join(' ')}`:\n\n$stderr'); - } - - String present() { - return ''' -Authority: $_codesignId -Time stamp: $_codesignTimestamp -Format: $_format -Signature size: $_signatureSize -Notarization: $_notarizationStatus -'''; - } - - /// Display overall information, intended to be machine parseable - /// - /// Output is of the format: - /// - /// Executable=/Users/developer/Downloads/mybinary - /// Identifier=mybinary - /// Format=Mach-O thin (x86_64) - /// CodeDirectory v=20500 size=38000 flags=0x10000(runtime) hashes=1177+7 location=embedded - /// Signature size=8979 - /// Authority=Developer ID Application: Dev Shop ABC (ABCC0VV123) - /// Authority=Developer ID Certification Authority - /// Authority=Apple Root CA - /// Timestamp=Jan 9, 2023 at 9:39:07 AM - /// Info.plist=not bound - /// TeamIdentifier=ABCC0VV123 - /// Runtime Version=13.1.0 - /// Sealed Resources=none - /// Internal requirements count=1 size=164 - Future _codesignDisplay() async { - final List command = [ - 'codesign', - '--display', - '-vv', - binaryPath, - ]; - final io.ProcessResult result = await pm.run(command); - - if (result.exitCode == 1) { - logger.severe(''' -File $binaryPath is not codesigned. To manually verify, run: - -codesign --display -vv $binaryPath -'''); - return false; - } else if (result.exitCode != 0) { - throw Exception( - 'Command `${command.join(' ')}` failed with code ${result.exitCode}\n\n' - '${result.stderr}', - ); - } - - final List lines = result.stderr.toString().trim().split('\n'); - for (final String line in lines) { - if (line.trim().isEmpty) { - continue; - } - final List segments = line.split('='); - final String name = segments.first; - - switch (name) { - case 'Executable': - case 'Identifier': - case 'CodeDirectory v': - case 'Info.plist': - // TeamIdentifier is redundant with the Authority field - case 'TeamIdentifier': - case 'Runtime Version': - case 'Sealed Resources': - case 'Internal requirements count': - break; - case 'Signature size': - _signatureSize = segments.sublist(1).join(); - break; - case 'Authority': - if (segments[1].startsWith('Developer ID Application')) { - _codesignId = segments[1]; - } - break; - case 'Timestamp': - _codesignTimestamp = segments[1]; - break; - case 'Format': - _format = segments.sublist(1).join(); - break; - default: - logger.warning( - 'Do not know how to parse a $name, skipping this field.', - ); - } - } - return true; - } -} diff --git a/cipd_packages/codesign/pubspec.yaml b/cipd_packages/codesign/pubspec.yaml deleted file mode 100644 index c2e12d21a..000000000 --- a/cipd_packages/codesign/pubspec.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: codesign -description: A standalone app to codesign Mac engine binaries. -version: 1.0.0 -homepage: https://github.com/flutter/cocoon - -environment: - sdk: '>=2.18.0 <4.0.0' - -dev_dependencies: - lints: 3.0.0 - test: 1.24.9 -dependencies: - archive: 3.4.9 - args: 2.4.2 - crypto: 3.0.3 - fake_async: 1.3.1 - file: 7.0.0 - flutter_lints: 3.0.1 - logging: 1.2.0 - meta: 1.11.0 - platform: 3.1.3 - process: 5.0.1 diff --git a/cipd_packages/codesign/test/file_codesign_visitor_test.dart b/cipd_packages/codesign/test/file_codesign_visitor_test.dart deleted file mode 100644 index 0b5e87bc7..000000000 --- a/cipd_packages/codesign/test/file_codesign_visitor_test.dart +++ /dev/null @@ -1,1249 +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:codesign/codesign.dart' as cs; -import 'package:codesign/src/log.dart'; -import 'package:codesign/src/utils.dart'; -import 'package:file/file.dart'; -import 'package:file/memory.dart'; -import 'package:logging/logging.dart'; -import 'package:test/test.dart'; - -import './src/fake_process_manager.dart'; - -void main() { - const String randomString = 'abcd1234'; - const String appSpecificPasswordFilePath = '/tmp/passwords.txt'; - const String codesignAppstoreIDFilePath = '/tmp/appID.txt'; - const String codesignTeamIDFilePath = '/tmp/teamID.txt'; - const String inputZipPath = '/tmp/input.zip'; - const String outputZipPath = '/tmp/output.zip'; - final List records = []; - - late MemoryFileSystem fileSystem; - late FakeProcessManager processManager; - late cs.FileCodesignVisitor codesignVisitor; - late Directory rootDirectory; - - setUp(() { - fileSystem = MemoryFileSystem.test(); - rootDirectory = fileSystem.systemTempDirectory.createTempSync('conductor_codesign'); - processManager = FakeProcessManager.list([]); - records.clear(); - log.onRecord.listen((LogRecord record) => records.add(record)); - }); - - group('test reading in passwords: ', () { - setUp(() { - codesignVisitor = cs.FileCodesignVisitor( - codesignCertName: randomString, - fileSystem: fileSystem, - appSpecificPasswordFilePath: appSpecificPasswordFilePath, - codesignAppstoreIDFilePath: codesignAppstoreIDFilePath, - codesignTeamIDFilePath: codesignTeamIDFilePath, - processManager: processManager, - rootDirectory: rootDirectory, - inputZipPath: inputZipPath, - outputZipPath: outputZipPath, - notarizationTimerDuration: const Duration(seconds: 0), - dryrun: false, - ); - codesignVisitor.directoriesVisited.clear(); - }); - - test('lacking password file throws exception', () async { - expect( - () async { - await codesignVisitor.readPassword(appSpecificPasswordFilePath); - }, - throwsA( - isA(), - ), - ); - }); - - test('providing correctly formatted password returns normally', () async { - fileSystem.file(appSpecificPasswordFilePath) - ..createSync(recursive: true, exclusive: true) - ..writeAsStringSync( - '123', - mode: FileMode.write, - encoding: utf8, - ); - - expect( - () async { - await codesignVisitor.readPassword(appSpecificPasswordFilePath); - await fileSystem.file(appSpecificPasswordFilePath).delete(); - }, - returnsNormally, - ); - }); - }); - - group('test utils function to join virtual entitlement path: ', () { - test('omits slash for the first path', () async { - expect(joinEntitlementPaths('', randomString), randomString); - }); - - test('concat with slash', () async { - expect(joinEntitlementPaths(randomString, randomString), '$randomString/$randomString'); - }); - }); - - group('test google cloud storage and processRemoteZip workflow', () { - setUp(() { - codesignVisitor = cs.FileCodesignVisitor( - codesignCertName: randomString, - fileSystem: fileSystem, - appSpecificPasswordFilePath: appSpecificPasswordFilePath, - codesignAppstoreIDFilePath: codesignAppstoreIDFilePath, - codesignTeamIDFilePath: codesignTeamIDFilePath, - processManager: processManager, - rootDirectory: rootDirectory, - notarizationTimerDuration: const Duration(seconds: 0), - dryrun: false, - inputZipPath: inputZipPath, - outputZipPath: outputZipPath, - ); - codesignVisitor.directoriesVisited.clear(); - codesignVisitor.appSpecificPassword = randomString; - codesignVisitor.codesignAppstoreId = randomString; - codesignVisitor.codesignTeamId = randomString; - }); - - test('procesRemotezip triggers correct workflow', () async { - final String zipFileName = '${rootDirectory.path}/remote_zip_4/folder_1/zip_1'; - fileSystem.file(zipFileName).createSync(recursive: true); - processManager.addCommands([ - FakeCommand( - command: [ - 'unzip', - codesignVisitor.inputZipPath, - '-d', - '${rootDirectory.absolute.path}/single_artifact', - ], - onRun: () => fileSystem - ..file('${rootDirectory.path}/single_artifact/entitlements.txt').createSync(recursive: true) - ..file('${rootDirectory.path}/single_artifact/without_entitlements.txt').createSync(recursive: true), - ), - FakeCommand( - command: [ - 'zip', - '--symlinks', - '--recurse-paths', - codesignVisitor.outputZipPath, - '.', - '--include', - '*', - ], - ), - FakeCommand( - command: [ - 'xcrun', - 'notarytool', - 'submit', - codesignVisitor.outputZipPath, - '--apple-id', - randomString, - '--password', - randomString, - '--team-id', - randomString, - ], - stdout: 'id: $randomString', - ), - const FakeCommand( - command: [ - 'xcrun', - 'notarytool', - 'info', - randomString, - '--password', - randomString, - '--apple-id', - randomString, - '--team-id', - randomString, - ], - stdout: 'status: Accepted', - ), - ]); - - await codesignVisitor.processRemoteZip(); - final Set messages = records - .where((LogRecord record) => record.level == Level.INFO) - .map((LogRecord record) => record.message) - .toSet(); - expect( - messages, - contains( - 'The downloaded file is unzipped from ${codesignVisitor.inputZipPath} to ${rootDirectory.path}/single_artifact', - ), - ); - expect( - messages, - contains('Visiting directory ${rootDirectory.absolute.path}/single_artifact'), - ); - expect( - messages, - contains('parsed binaries with entitlements are {}'), - ); - expect( - messages, - contains('parsed binaries without entitlements are {}'), - ); - expect( - messages, - contains( - 'uploading xcrun notarytool submit ${codesignVisitor.outputZipPath} --apple-id $randomString --password $randomString --team-id $randomString', - ), - ); - expect( - messages, - contains('RequestUUID for ${codesignVisitor.outputZipPath} is: $randomString'), - ); - expect( - messages, - contains( - 'checking notary status with xcrun notarytool info $randomString --password $randomString --apple-id $randomString --team-id $randomString', - ), - ); - expect( - messages, - contains('successfully notarized ${codesignVisitor.outputZipPath}'), - ); - }); - }); - - group('visit directory/zip api calls: ', () { - setUp(() { - codesignVisitor = cs.FileCodesignVisitor( - codesignCertName: randomString, - fileSystem: fileSystem, - appSpecificPasswordFilePath: appSpecificPasswordFilePath, - codesignAppstoreIDFilePath: codesignAppstoreIDFilePath, - codesignTeamIDFilePath: codesignTeamIDFilePath, - processManager: processManager, - rootDirectory: rootDirectory, - inputZipPath: inputZipPath, - outputZipPath: outputZipPath, - notarizationTimerDuration: Duration.zero, - ); - codesignVisitor.directoriesVisited.clear(); - codesignVisitor.appSpecificPassword = randomString; - codesignVisitor.codesignAppstoreId = randomString; - codesignVisitor.codesignTeamId = randomString; - }); - - test('visitDirectory correctly list files', () async { - fileSystem - ..file('${rootDirectory.path}/remote_zip_0/file_a').createSync(recursive: true) - ..file('${rootDirectory.path}/remote_zip_0/file_b').createSync(recursive: true) - ..file('${rootDirectory.path}/remote_zip_0/file_c').createSync(recursive: true); - processManager.addCommands([ - FakeCommand( - command: [ - 'file', - '--mime-type', - '-b', - '${rootDirectory.absolute.path}/remote_zip_0/file_a', - ], - stdout: 'other_files', - ), - FakeCommand( - command: [ - 'file', - '--mime-type', - '-b', - '${rootDirectory.absolute.path}/remote_zip_0/file_b', - ], - stdout: 'other_files', - ), - FakeCommand( - command: [ - 'file', - '--mime-type', - '-b', - '${rootDirectory.absolute.path}/remote_zip_0/file_c', - ], - stdout: 'other_files', - ), - ]); - final Directory testDirectory = fileSystem.directory('${rootDirectory.path}/remote_zip_0'); - await codesignVisitor.visitDirectory( - directory: testDirectory, - parentVirtualPath: 'a.zip', - ); - final List messages = records - .where((LogRecord record) => record.level == Level.INFO) - .map((LogRecord record) => record.message) - .toList(); - expect(messages, contains('Visiting directory ${rootDirectory.path}/remote_zip_0')); - expect(messages, contains('Child file of directory remote_zip_0 is file_a')); - expect(messages, contains('Child file of directory remote_zip_0 is file_b')); - expect(messages, contains('Child file of directory remote_zip_0 is file_c')); - }); - - test('visitDirectory recursively visits directory', () async { - fileSystem - ..file('${rootDirectory.path}/remote_zip_1/file_a').createSync(recursive: true) - ..file('${rootDirectory.path}/remote_zip_1/folder_a/file_b').createSync(recursive: true); - final Directory testDirectory = fileSystem.directory('${rootDirectory.path}/remote_zip_1'); - processManager.addCommands([ - FakeCommand( - command: [ - 'file', - '--mime-type', - '-b', - '${rootDirectory.absolute.path}/remote_zip_1/file_a', - ], - stdout: 'other_files', - ), - FakeCommand( - command: [ - 'file', - '--mime-type', - '-b', - '${rootDirectory.absolute.path}/remote_zip_1/folder_a/file_b', - ], - stdout: 'other_files', - ), - ]); - await codesignVisitor.visitDirectory( - directory: testDirectory, - parentVirtualPath: '', - ); - final List messages = records - .where((LogRecord record) => record.level == Level.INFO) - .map((LogRecord record) => record.message) - .toList(); - expect(messages, contains('Visiting directory ${rootDirectory.path}/remote_zip_1')); - expect(messages, contains('Visiting directory ${rootDirectory.path}/remote_zip_1/folder_a')); - expect(messages, contains('Child file of directory remote_zip_1 is file_a')); - expect(messages, contains('Child file of directory folder_a is file_b')); - }); - - test('visit directory inside a zip', () async { - final String zipFileName = '${rootDirectory.path}/remote_zip_2/zip_1'; - fileSystem.file(zipFileName).createSync(recursive: true); - processManager.addCommands([ - FakeCommand( - command: [ - 'unzip', - '${rootDirectory.absolute.path}/remote_zip_2/zip_1', - '-d', - '${rootDirectory.absolute.path}/embedded_zip_${zipFileName.hashCode}', - ], - onRun: () => fileSystem - ..file('${rootDirectory.path}/embedded_zip_${zipFileName.hashCode}/file_1').createSync(recursive: true) - ..file('${rootDirectory.path}/embedded_zip_${zipFileName.hashCode}/file_2').createSync(recursive: true), - ), - FakeCommand( - command: [ - 'file', - '--mime-type', - '-b', - '${rootDirectory.absolute.path}/embedded_zip_${zipFileName.hashCode}/file_1', - ], - stdout: 'other_files', - ), - FakeCommand( - command: [ - 'file', - '--mime-type', - '-b', - '${rootDirectory.absolute.path}/embedded_zip_${zipFileName.hashCode}/file_2', - ], - stdout: 'other_files', - ), - FakeCommand( - command: [ - 'zip', - '--symlinks', - '--recurse-paths', - '${rootDirectory.absolute.path}/remote_zip_2/zip_1', - '.', - '--include', - '*', - ], - onRun: () => fileSystem.file('${rootDirectory.path}/remote_zip_2/zip_1').createSync(recursive: true), - ), - ]); - - await codesignVisitor.visitEmbeddedZip( - zipEntity: fileSystem.file('${rootDirectory.path}/remote_zip_2/zip_1'), - parentVirtualPath: 'a.zip', - ); - final List messages = records - .where((LogRecord record) => record.level == Level.INFO) - .map((LogRecord record) => record.message) - .toList(); - expect( - messages, - contains( - 'The downloaded file is unzipped from ${rootDirectory.path}/remote_zip_2/zip_1 to ${rootDirectory.path}/embedded_zip_${zipFileName.hashCode}', - ), - ); - expect(messages, contains('Visiting directory ${rootDirectory.path}/embedded_zip_${zipFileName.hashCode}')); - expect(messages, contains('Child file of directory embedded_zip_${zipFileName.hashCode} is file_1')); - expect(messages, contains('Child file of directory embedded_zip_${zipFileName.hashCode} is file_2')); - }); - - test('visit zip inside a directory', () async { - final String zipFileName = '${rootDirectory.path}/remote_zip_4/folder_1/zip_1'; - fileSystem.file(zipFileName).createSync(recursive: true); - processManager.addCommands([ - FakeCommand( - command: [ - 'file', - '--mime-type', - '-b', - '${rootDirectory.absolute.path}/remote_zip_4/folder_1/zip_1', - ], - stdout: 'application/zip', - ), - FakeCommand( - command: [ - 'unzip', - '${rootDirectory.absolute.path}/remote_zip_4/folder_1/zip_1', - '-d', - '${rootDirectory.absolute.path}/embedded_zip_${zipFileName.hashCode}', - ], - onRun: () => fileSystem - .directory('${rootDirectory.path}/embedded_zip_${zipFileName.hashCode}') - .createSync(recursive: true), - ), - FakeCommand( - command: [ - 'zip', - '--symlinks', - '--recurse-paths', - '${rootDirectory.absolute.path}/remote_zip_4/folder_1/zip_1', - '.', - '--include', - '*', - ], - ), - ]); - - await codesignVisitor.visitDirectory( - directory: fileSystem.directory('${rootDirectory.path}/remote_zip_4'), - parentVirtualPath: 'a.zip', - ); - final List messages = records - .where((LogRecord record) => record.level == Level.INFO) - .map((LogRecord record) => record.message) - .toList(); - expect(messages, contains('Visiting directory ${rootDirectory.absolute.path}/remote_zip_4')); - expect(messages, contains('Visiting directory ${rootDirectory.absolute.path}/remote_zip_4/folder_1')); - expect( - messages, - contains( - 'The downloaded file is unzipped from ${rootDirectory.path}/remote_zip_4/folder_1/zip_1 to ${rootDirectory.path}/embedded_zip_${zipFileName.hashCode}', - ), - ); - expect( - messages, - contains('Visiting directory ${rootDirectory.absolute.path}/embedded_zip_${zipFileName.hashCode}'), - ); - }); - - test('throw exception when the same directory is visited', () async { - fileSystem.file('${rootDirectory.path}/parent_1/child_1/file_1').createSync(recursive: true); - processManager.addCommands([ - FakeCommand( - command: [ - 'file', - '--mime-type', - '-b', - '${rootDirectory.absolute.path}/parent_1/child_1/file_1', - ], - stdout: 'other_files', - ), - FakeCommand( - command: [ - 'file', - '--mime-type', - '-b', - '${rootDirectory.absolute.path}/parent_1/child_1/file_1', - ], - stdout: 'other_files', - ), - ]); - - await codesignVisitor.visitDirectory( - directory: fileSystem.directory('${rootDirectory.path}/parent_1/child_1'), - parentVirtualPath: 'a.zip', - ); - List warnings = records - .where((LogRecord record) => record.level == Level.WARNING) - .map((LogRecord record) => record.message) - .toList(); - expect(warnings, isEmpty); - - await codesignVisitor.visitDirectory( - directory: fileSystem.directory('${rootDirectory.path}/parent_1'), - parentVirtualPath: 'a.zip', - ); - warnings = records - .where((LogRecord record) => record.level == Level.WARNING) - .map((LogRecord record) => record.message) - .toList(); - expect( - warnings, - contains( - 'Warning! You are visiting a directory that has been visited before, the directory is ${rootDirectory.path}/parent_1/child_1', - ), - ); - }); - - test('visitDirectory skips file or directory that is a symlink', () async { - fileSystem - ..file('${rootDirectory.path}/remote_zip_5/target_dir/file_b').createSync(recursive: true) - ..directory('${rootDirectory.path}/remote_zip_5/symlink_dir').createSync(recursive: true) - ..link('${rootDirectory.path}/remote_zip_5/symlink_dir/file_a') - .createSync('${rootDirectory.path}/remote_zip_5/target_dir/file_b') - ..link('${rootDirectory.path}/remote_zip_5/symlink_dir_2') - .createSync('${rootDirectory.path}/remote_zip_5/target_dir'); - processManager.addCommands([ - FakeCommand( - command: [ - 'file', - '--mime-type', - '-b', - '${rootDirectory.absolute.path}/remote_zip_5/target_dir/file_b', - ], - stdout: 'other_files', - ), - ]); - final Directory testDirectory = fileSystem.directory('${rootDirectory.path}/remote_zip_5'); - await codesignVisitor.visitDirectory( - directory: testDirectory, - parentVirtualPath: 'a.zip', - ); - final Set messages = records - .where((LogRecord record) => record.level == Level.INFO) - .map((LogRecord record) => record.message) - .toSet(); - expect(messages, contains('Visiting directory ${rootDirectory.path}/remote_zip_5/target_dir')); - expect(messages, contains('Child file of directory target_dir is file_b')); - - // Skip code signing a file that is a symlink. - expect(messages, contains('Visiting directory ${rootDirectory.path}/remote_zip_5/symlink_dir')); - expect( - messages, - contains('current file or direcotry ${rootDirectory.path}/remote_zip_5/symlink_dir/file_a is a symlink to ' - '${rootDirectory.path}/remote_zip_5/target_dir/file_b, codesign is therefore skipped for the current file or directory.'), - ); - expect(messages, isNot(contains('Child file of directory symlink_dir is file_a'))); - - // Skip code signing a directory that is a symlink. - expect( - messages, - contains('current file or direcotry ${rootDirectory.path}/remote_zip_5/symlink_dir_2 is a symlink to ' - '${rootDirectory.path}/remote_zip_5/target_dir, codesign is therefore skipped for the current file or directory.'), - ); - }); - - test('visitBinary codesigns binary with / without entitlement', () async { - codesignVisitor = cs.FileCodesignVisitor( - codesignCertName: randomString, - fileSystem: fileSystem, - appSpecificPasswordFilePath: appSpecificPasswordFilePath, - codesignAppstoreIDFilePath: codesignAppstoreIDFilePath, - codesignTeamIDFilePath: codesignTeamIDFilePath, - processManager: processManager, - rootDirectory: rootDirectory, - inputZipPath: inputZipPath, - outputZipPath: outputZipPath, - dryrun: false, - notarizationTimerDuration: const Duration(seconds: 0), - ); - codesignVisitor.appSpecificPassword = randomString; - codesignVisitor.codesignAppstoreId = randomString; - codesignVisitor.codesignTeamId = randomString; - codesignVisitor.fileWithEntitlements = {'root/folder_a/file_a'}; - codesignVisitor.fileWithoutEntitlements = {'root/folder_b/file_b'}; - fileSystem - ..file('${rootDirectory.path}/remote_zip_6/folder_a/file_a').createSync(recursive: true) - ..file('${rootDirectory.path}/remote_zip_6/folder_b/file_b').createSync(recursive: true); - final Directory testDirectory = fileSystem.directory('${rootDirectory.path}/remote_zip_6'); - processManager.addCommands([ - FakeCommand( - command: [ - 'file', - '--mime-type', - '-b', - '${rootDirectory.absolute.path}/remote_zip_6/folder_a/file_a', - ], - stdout: 'application/x-mach-binary', - ), - FakeCommand( - command: [ - '/usr/bin/codesign', - '--keychain', - 'build.keychain', - '-f', - '-s', - randomString, - '${rootDirectory.absolute.path}/remote_zip_6/folder_a/file_a', - '--timestamp', - '--options=runtime', - '--entitlements', - '${rootDirectory.absolute.path}/Entitlements.plist', - ], - ), - FakeCommand( - command: [ - 'file', - '--mime-type', - '-b', - '${rootDirectory.absolute.path}/remote_zip_6/folder_b/file_b', - ], - stdout: 'application/x-mach-binary', - ), - FakeCommand( - command: [ - '/usr/bin/codesign', - '--keychain', - 'build.keychain', - '-f', - '-s', - randomString, - '${rootDirectory.absolute.path}/remote_zip_6/folder_b/file_b', - '--timestamp', - '--options=runtime', - ], - ), - ]); - await codesignVisitor.visitDirectory( - directory: testDirectory, - parentVirtualPath: 'root', - ); - final List messages = records - .where((LogRecord record) => record.level == Level.INFO) - .map((LogRecord record) => record.message) - .toList(); - expect(messages, contains('signing file at path ${rootDirectory.absolute.path}/remote_zip_6/folder_a/file_a')); - expect(messages, contains('the virtual entitlement path associated with file is root/folder_a/file_a')); - expect(messages, contains('the decision to sign with entitlement is true')); - - expect(messages, contains('signing file at path ${rootDirectory.absolute.path}/remote_zip_6/folder_b/file_b')); - expect(messages, contains('the virtual entitlement path associated with file is root/folder_b/file_b')); - expect(messages, contains('the decision to sign with entitlement is false')); - }); - }); - - group('parse entitlement configs: ', () { - setUp(() { - codesignVisitor = cs.FileCodesignVisitor( - codesignCertName: randomString, - inputZipPath: inputZipPath, - outputZipPath: outputZipPath, - fileSystem: fileSystem, - appSpecificPasswordFilePath: appSpecificPasswordFilePath, - codesignAppstoreIDFilePath: codesignAppstoreIDFilePath, - codesignTeamIDFilePath: codesignTeamIDFilePath, - processManager: processManager, - rootDirectory: rootDirectory, - ); - codesignVisitor.directoriesVisited.clear(); - codesignVisitor.appSpecificPassword = randomString; - codesignVisitor.codesignAppstoreId = randomString; - codesignVisitor.codesignTeamId = randomString; - }); - - test('correctly store file paths', () async { - fileSystem.file('${rootDirectory.absolute.path}/test_entitlement/entitlements.txt') - ..createSync(recursive: true) - ..writeAsStringSync( - '''file_a -file_b -file_c''', - mode: FileMode.append, - encoding: utf8, - ); - - fileSystem.file('${rootDirectory.absolute.path}/test_entitlement/without_entitlements.txt') - ..createSync(recursive: true) - ..writeAsStringSync( - '''file_d -file_e''', - mode: FileMode.append, - encoding: utf8, - ); - final Set fileWithEntitlements = await codesignVisitor.parseEntitlements( - fileSystem.directory('${rootDirectory.absolute.path}/test_entitlement'), - true, - ); - final Set fileWithoutEntitlements = await codesignVisitor.parseEntitlements( - fileSystem.directory('${rootDirectory.absolute.path}/test_entitlement'), - false, - ); - expect(fileWithEntitlements.length, 3); - expect( - fileWithEntitlements, - containsAll([ - 'file_a', - 'file_b', - 'file_c', - ]), - ); - expect(fileWithoutEntitlements.length, 2); - expect( - fileWithoutEntitlements, - containsAll([ - 'file_d', - 'file_e', - ]), - ); - }); - - test('log warnings when configuration file is missing', () async { - fileSystem.file('${rootDirectory.absolute.path}/test_entitlement_2/entitlements.txt') - ..createSync(recursive: true) - ..writeAsStringSync( - '''file_a -file_b -file_c''', - mode: FileMode.append, - encoding: utf8, - ); - - final Set fileWithEntitlements = await codesignVisitor.parseEntitlements( - fileSystem.directory('${rootDirectory.absolute.path}/test_entitlement_2'), - true, - ); - expect(fileWithEntitlements.length, 3); - expect( - fileWithEntitlements, - containsAll([ - 'file_a', - 'file_b', - 'file_c', - ]), - ); - await codesignVisitor.parseEntitlements( - fileSystem.directory('${rootDirectory.absolute.path}/test_entitlement_2'), - false, - ); - final List messages = records - .where((LogRecord record) => record.level == Level.WARNING) - .map((LogRecord record) => record.message) - .toList(); - expect( - messages, - contains('${rootDirectory.absolute.path}/test_entitlement_2/without_entitlements.txt not found. ' - 'by default, system will assume there is no without_entitlements file. ' - 'As a result, no binary will be codesigned.' - 'if this is not intended, please provide them along with the engine artifacts.'), - ); - }); - }); - - group('notarization tests: ', () { - setUp(() { - codesignVisitor = cs.FileCodesignVisitor( - codesignCertName: randomString, - inputZipPath: inputZipPath, - outputZipPath: outputZipPath, - fileSystem: fileSystem, - appSpecificPasswordFilePath: appSpecificPasswordFilePath, - codesignAppstoreIDFilePath: codesignAppstoreIDFilePath, - codesignTeamIDFilePath: codesignTeamIDFilePath, - processManager: processManager, - rootDirectory: rootDirectory, - ); - codesignVisitor.directoriesVisited.clear(); - codesignVisitor.appSpecificPassword = randomString; - codesignVisitor.codesignAppstoreId = randomString; - codesignVisitor.codesignTeamId = randomString; - }); - - test('successful notarization check returns true', () async { - processManager.addCommands([ - const FakeCommand( - command: [ - 'xcrun', - 'notarytool', - 'info', - randomString, - '--password', - randomString, - '--apple-id', - randomString, - '--team-id', - randomString, - ], - stdout: '''createdDate: 2021-04-29T01:38:09.498Z -id: 2efe2717-52ef-43a5-96dc-0797e4ca1041 -name: OvernightTextEditor_11.6.8.zip -status: Accepted''', - ), - ]); - - expect( - codesignVisitor.checkNotaryJobFinished(randomString), - true, - ); - }); - - test('wrong format (such as altool) check throws exception', () async { - processManager.addCommands([ - const FakeCommand( - command: [ - 'xcrun', - 'notarytool', - 'info', - randomString, - '--password', - randomString, - '--apple-id', - randomString, - '--team-id', - randomString, - ], - stdout: '''RequestUUID: 2EFE2717-52EF-43A5-96DC-0797E4CA1041 -Date: 2021-07-02 20:32:01 +0000 -Status: invalid -LogFileURL: https://osxapps.itunes.apple.com/... -Status Code: 2 -Status Message: Package Invalid''', - ), - ]); - - expect( - () => codesignVisitor.checkNotaryJobFinished(randomString), - throwsA( - isA(), - ), - ); - }); - - test('in progress notarization check returns false', () async { - processManager.addCommands([ - const FakeCommand( - command: [ - 'xcrun', - 'notarytool', - 'info', - randomString, - '--password', - randomString, - '--apple-id', - randomString, - '--team-id', - randomString, - ], - stdout: '''createdDate: 2021-04-29T01:38:09.498Z -id: 2efe2717-52ef-43a5-96dc-0797e4ca1041 -name: OvernightTextEditor_11.6.8.zip -status: In Progress''', - ), - ]); - - expect( - codesignVisitor.checkNotaryJobFinished(randomString), - false, - ); - }); - - test('invalid status check throws exception', () async { - processManager.addCommands([ - const FakeCommand( - command: [ - 'xcrun', - 'notarytool', - 'info', - randomString, - '--password', - randomString, - '--apple-id', - randomString, - '--team-id', - randomString, - ], - stdout: '''createdDate: 2021-04-29T01:38:09.498Z -id: 2efe2717-52ef-43a5-96dc-0797e4ca1041 -name: OvernightTextEditor_11.6.8.zip -status: Invalid''', - ), - ]); - - expect( - () => codesignVisitor.checkNotaryJobFinished(randomString), - throwsA( - isA(), - ), - ); - }); - - test('upload notary retries upon failure', () async { - fileSystem.file('${rootDirectory.absolute.path}/temp').createSync(); - processManager.addCommands([ - FakeCommand( - command: [ - 'xcrun', - 'notarytool', - 'submit', - '${rootDirectory.absolute.path}/temp', - '--apple-id', - randomString, - '--password', - randomString, - '--team-id', - randomString, - ], - stdout: '''Error uploading file. - Id: something that causes failure - path: /Users/flutter/Desktop/OvernightTextEditor_11.6.8.zip''', - ), - FakeCommand( - command: [ - 'xcrun', - 'notarytool', - 'submit', - '${rootDirectory.absolute.path}/temp', - '--apple-id', - randomString, - '--password', - randomString, - '--team-id', - randomString, - ], - stdout: '''Successfully uploaded file. - id: 2efe2717-52ef-43a5-96dc-0797e4ca1041 - path: /Users/flutter/Desktop/OvernightTextEditor_11.6.8.zip''', - ), - ]); - - final String uuid = codesignVisitor.uploadZipToNotary( - fileSystem.file('${rootDirectory.absolute.path}/temp'), - 3, - 0, - ); - expect(uuid, '2efe2717-52ef-43a5-96dc-0797e4ca1041'); - final List messages = records - .where((LogRecord record) => record.level == Level.WARNING) - .map((LogRecord record) => record.message) - .toList(); - expect( - messages, - contains('Failed to upload to the notary service with args: ' - 'xcrun notarytool submit ${rootDirectory.absolute.path}/temp ' - '--apple-id abcd1234 --password abcd1234 --team-id abcd1234'), - ); - expect( - messages, - contains('Trying again 2 more times...'), - ); - }); - - test('upload notary throws exception if exit code is unnormal', () async { - fileSystem.file('${rootDirectory.absolute.path}/temp').createSync(); - processManager.addCommands([ - FakeCommand( - command: [ - 'xcrun', - 'notarytool', - 'submit', - '${rootDirectory.absolute.path}/temp', - '--apple-id', - randomString, - '--password', - randomString, - '--team-id', - randomString, - ], - stdout: '''Error uploading file. - Id: something that causes failure - path: /Users/flutter/Desktop/OvernightTextEditor_11.6.8.zip''', - exitCode: -1, - ), - ]); - - expect( - () => codesignVisitor.uploadZipToNotary( - fileSystem.file('${rootDirectory.absolute.path}/temp'), - 1, - 0, - ), - throwsA( - isA(), - ), - ); - }); - - test('upload notary throws exception after 3 default tries', () async { - fileSystem.file('${rootDirectory.absolute.path}/temp').createSync(); - processManager.addCommands([ - FakeCommand( - command: [ - 'xcrun', - 'notarytool', - 'submit', - '${rootDirectory.absolute.path}/temp', - '--apple-id', - randomString, - '--password', - randomString, - '--team-id', - randomString, - ], - stdout: '''Error uploading file. - Id: something that causes failure - path: /Users/flutter/Desktop/OvernightTextEditor_11.6.8.zip''', - ), - FakeCommand( - command: [ - 'xcrun', - 'notarytool', - 'submit', - '${rootDirectory.absolute.path}/temp', - '--apple-id', - randomString, - '--password', - randomString, - '--team-id', - randomString, - ], - stdout: '''Error uploading file. - Id: something that causes failure - path: /Users/flutter/Desktop/OvernightTextEditor_11.6.8.zip''', - ), - FakeCommand( - command: [ - 'xcrun', - 'notarytool', - 'submit', - '${rootDirectory.absolute.path}/temp', - '--apple-id', - randomString, - '--password', - randomString, - '--team-id', - randomString, - ], - stdout: '''Error uploading file. - Id: something that causes failure - path: /Users/flutter/Desktop/OvernightTextEditor_11.6.8.zip''', - ), - ]); - - expect( - () => codesignVisitor.uploadZipToNotary( - fileSystem.file('${rootDirectory.absolute.path}/temp'), - 3, - 0, - ), - throwsA( - isA(), - ), - ); - final List messages = records - .where((LogRecord record) => record.level == Level.WARNING) - .map((LogRecord record) => record.message) - .toList(); - expect( - messages, - contains('The upload to notary service failed after retries, and' - ' the output format does not match the current notary tool version.' - ' If after inspecting the output, you believe the process finished ' - 'successfully but was not detected, please contact flutter release engineers'), - ); - }); - }); - - group('support optional switches and dryrun :', () { - setUp(() { - codesignVisitor = cs.FileCodesignVisitor( - codesignCertName: randomString, - inputZipPath: inputZipPath, - outputZipPath: outputZipPath, - fileSystem: fileSystem, - appSpecificPasswordFilePath: appSpecificPasswordFilePath, - codesignAppstoreIDFilePath: codesignAppstoreIDFilePath, - codesignTeamIDFilePath: codesignTeamIDFilePath, - processManager: processManager, - rootDirectory: rootDirectory, - notarizationTimerDuration: const Duration(seconds: 0), - ); - codesignVisitor.directoriesVisited.clear(); - codesignVisitor.appSpecificPassword = randomString; - codesignVisitor.codesignAppstoreId = randomString; - codesignVisitor.codesignTeamId = randomString; - fileSystem.file(codesignAppstoreIDFilePath) - ..createSync(recursive: true) - ..writeAsStringSync(randomString); - fileSystem.file(codesignTeamIDFilePath) - ..createSync(recursive: true) - ..writeAsStringSync(randomString); - fileSystem.file(appSpecificPasswordFilePath) - ..createSync(recursive: true) - ..writeAsStringSync(randomString); - }); - - test('codesign optional switches artifacts when dryrun is true', () async { - processManager.addCommands([ - FakeCommand( - command: [ - 'unzip', - codesignVisitor.inputZipPath, - '-d', - '${rootDirectory.absolute.path}/single_artifact', - ], - onRun: () => fileSystem - ..file('${rootDirectory.path}/single_artifact/entitlements.txt').createSync(recursive: true) - ..file('${rootDirectory.path}/single_artifact/without_entitlements.txt').createSync(recursive: true), - ), - FakeCommand( - command: [ - 'zip', - '--symlinks', - '--recurse-paths', - codesignVisitor.outputZipPath, - '.', - '--include', - '*', - ], - ), - FakeCommand( - command: [ - 'xcrun', - 'notarytool', - 'submit', - codesignVisitor.outputZipPath, - '--apple-id', - randomString, - '--password', - randomString, - '--team-id', - randomString, - ], - stdout: 'id: $randomString', - ), - const FakeCommand( - command: [ - 'xcrun', - 'notarytool', - 'info', - randomString, - '--password', - randomString, - '--apple-id', - randomString, - '--team-id', - randomString, - ], - stdout: 'status: Accepted', - ), - ]); - await codesignVisitor.validateAll(); - final List messages = records - .where((LogRecord record) => record.level == Level.INFO) - .map((LogRecord record) => record.message) - .toList(); - expect( - messages, - contains('code signing dry run has completed, this is a quick sanity check without' - 'going through the notary service. To run the full codesign process, use --no-dryrun flag.'), - ); - }); - - test('upload optional switch artifacts when dryrun is false', () async { - processManager.addCommands([ - FakeCommand( - command: [ - 'unzip', - codesignVisitor.inputZipPath, - '-d', - '${rootDirectory.absolute.path}/single_artifact', - ], - onRun: () => fileSystem - ..file('${rootDirectory.path}/single_artifact/entitlements.txt').createSync(recursive: true) - ..file('${rootDirectory.path}/single_artifact/without_entitlements.txt').createSync(recursive: true), - ), - FakeCommand( - command: [ - 'zip', - '--symlinks', - '--recurse-paths', - codesignVisitor.outputZipPath, - '.', - '--include', - '*', - ], - ), - FakeCommand( - command: [ - 'xcrun', - 'notarytool', - 'submit', - codesignVisitor.outputZipPath, - '--apple-id', - randomString, - '--password', - randomString, - '--team-id', - randomString, - ], - stdout: 'id: $randomString', - ), - const FakeCommand( - command: [ - 'xcrun', - 'notarytool', - 'info', - randomString, - '--password', - randomString, - '--apple-id', - randomString, - '--team-id', - randomString, - ], - stdout: 'status: Accepted', - ), - ]); - codesignVisitor = cs.FileCodesignVisitor( - codesignCertName: randomString, - inputZipPath: inputZipPath, - outputZipPath: outputZipPath, - fileSystem: fileSystem, - appSpecificPasswordFilePath: appSpecificPasswordFilePath, - codesignAppstoreIDFilePath: codesignAppstoreIDFilePath, - codesignTeamIDFilePath: codesignTeamIDFilePath, - processManager: processManager, - rootDirectory: rootDirectory, - notarizationTimerDuration: const Duration(seconds: 0), - dryrun: false, - ); - codesignVisitor.appSpecificPassword = randomString; - codesignVisitor.codesignAppstoreId = randomString; - codesignVisitor.codesignTeamId = randomString; - codesignVisitor.directoriesVisited.clear(); - await codesignVisitor.validateAll(); - final Set messages = records - .where((LogRecord record) => record.level == Level.INFO) - .map((LogRecord record) => record.message) - .toSet(); - expect( - messages, - contains('Codesign completed. Codesigned zip is located at ${codesignVisitor.outputZipPath}.' - 'If you have uploaded the artifacts back to google cloud storage, please delete' - ' the folder ${codesignVisitor.outputZipPath} and ${codesignVisitor.inputZipPath}.'), - ); - expect( - messages, - isNot( - contains('code signing dry run has completed, this is a quick sanity check without' - 'going through the notary service. To run the full codesign process, use --no-dryrun flag.'), - ), - ); - }); - }); -} diff --git a/cipd_packages/codesign/test/src/fake_process_manager.dart b/cipd_packages/codesign/test/src/fake_process_manager.dart deleted file mode 100644 index 9aca69ff0..000000000 --- a/cipd_packages/codesign/test/src/fake_process_manager.dart +++ /dev/null @@ -1,446 +0,0 @@ -// Copyright 2014 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' as io show ProcessSignal, Process, ProcessStartMode, ProcessResult, systemEncoding; - -import 'package:file/file.dart'; -import 'package:meta/meta.dart'; -import 'package:process/process.dart'; -import 'package:test/test.dart'; - -export 'package:process/process.dart' show ProcessManager; - -typedef VoidCallback = void Function(); - -/// A command for [FakeProcessManager]. -@immutable -class FakeCommand { - const FakeCommand({ - required this.command, - this.workingDirectory, - this.environment, - this.encoding, - this.duration = Duration.zero, - this.onRun, - this.exitCode = 0, - this.stdout = '', - this.stderr = '', - this.completer, - this.stdin, - this.exception, - this.outputFollowsExit = false, - }); - - /// The exact commands that must be matched for this [FakeCommand] to be - /// considered correct. - final List command; - - /// The exact working directory that must be matched for this [FakeCommand] to - /// be considered correct. - /// - /// If this is null, the working directory is ignored. - final String? workingDirectory; - - /// The environment that must be matched for this [FakeCommand] to be considered correct. - /// - /// If this is null, then the environment is ignored. - /// - /// Otherwise, each key in this environment must be present and must have a - /// value that matches the one given here for the [FakeCommand] to match. - final Map? environment; - - /// The stdout and stderr encoding that must be matched for this [FakeCommand] - /// to be considered correct. - /// - /// If this is null, then the encodings are ignored. - final Encoding? encoding; - - /// The time to allow to elapse before returning the [exitCode], if this command - /// is "executed". - /// - /// If you set this to a non-zero time, you should use a [FakeAsync] zone, - /// otherwise the test will be artificially slow. - final Duration duration; - - /// A callback that is run after [duration] expires but before the [exitCode] - /// (and output) are passed back. - final VoidCallback? onRun; - - /// The process' exit code. - /// - /// To simulate a never-ending process, set [duration] to a value greater than - /// 15 minutes (the timeout for our tests). - /// - /// To simulate a crash, subtract the crash signal number from 256. For example, - /// SIGPIPE (-13) is 243. - final int exitCode; - - /// The output to simulate on stdout. This will be encoded as UTF-8 and - /// returned in one go. - final String stdout; - - /// The output to simulate on stderr. This will be encoded as UTF-8 and - /// returned in one go. - final String stderr; - - /// If provided, allows the command completion to be blocked until the future - /// resolves. - final Completer? completer; - - /// An optional stdin sink that will be exposed through the resulting - /// [FakeProcess]. - final IOSink? stdin; - - /// If provided, this exception will be thrown when the fake command is run. - final Object? exception; - - /// Indicates that output will only be emitted after the `exitCode` [Future] - /// on [io.Process] completes. - final bool outputFollowsExit; - - void _matches( - List command, - String? workingDirectory, - Map? environment, - Encoding? encoding, - ) { - expect(command, equals(this.command)); - if (this.workingDirectory != null) { - expect(this.workingDirectory, workingDirectory); - } - if (this.environment != null) { - expect(this.environment, environment); - } - if (this.encoding != null) { - expect(this.encoding, encoding); - } - } -} - -class _FakeProcess implements io.Process { - _FakeProcess( - this._exitCode, - Duration duration, - this.pid, - this._stderr, - IOSink? stdin, - this._stdout, - this._completer, - bool outputFollowsExit, - ) : exitCode = Future.delayed(duration).then((void value) { - if (_completer != null) { - return _completer.future.then((void _) => _exitCode); - } - return _exitCode; - }), - stdin = stdin ?? IOSink(StreamController>().sink) { - if (_stderr == '') { - stderr = const Stream>.empty(); - } else if (outputFollowsExit) { - stderr = Stream>.fromFuture( - exitCode.then((_) { - return Future>(() => utf8.encode(_stderr)); - }), - ); - } else { - stderr = Stream>.value(utf8.encode(_stderr)); - } - - if (_stdout == '') { - stdout = const Stream>.empty(); - } else if (outputFollowsExit) { - stdout = Stream>.fromFuture( - exitCode.then((_) { - return Future>(() => utf8.encode(_stdout)); - }), - ); - } else { - stdout = Stream>.value(utf8.encode(_stdout)); - } - } - - final int _exitCode; - final Completer? _completer; - - @override - final Future exitCode; - - @override - final int pid; - - final String _stderr; - - @override - late final Stream> stderr; - - @override - final IOSink stdin; - - @override - late final Stream> stdout; - - final String _stdout; - - @override - bool kill([io.ProcessSignal signal = io.ProcessSignal.sigterm]) { - // Killing a fake process has no effect. - return false; - } -} - -abstract class FakeProcessManager implements ProcessManager { - /// A fake [ProcessManager] which responds to all commands as if they had run - /// instantaneously with an exit code of 0 and no output. - factory FakeProcessManager.any() = _FakeAnyProcessManager; - - /// A fake [ProcessManager] which responds to particular commands with - /// particular results. - /// - /// On creation, pass in a list of [FakeCommand] objects. When the - /// [ProcessManager] methods such as [start] are invoked, the next - /// [FakeCommand] must match (otherwise the test fails); its settings are used - /// to simulate the result of running that command. - /// - /// If no command is found, then one is implied which immediately returns exit - /// code 0 with no output. - /// - /// There is no logic to ensure that all the listed commands are run. Use - /// [FakeCommand.onRun] to set a flag, or specify a sentinel command as your - /// last command and verify its execution is successful, to ensure that all - /// the specified commands are actually called. - factory FakeProcessManager.list(List commands) = _SequenceProcessManager; - factory FakeProcessManager.empty() => _SequenceProcessManager([]); - - FakeProcessManager._(); - - /// Adds a new [FakeCommand] to the current process manager. - /// - /// This can be used to configure test expectations after the [ProcessManager] has been - /// provided to another interface. - /// - /// This is a no-op on [FakeProcessManager.any]. - void addCommand(FakeCommand command); - - /// Add multiple [FakeCommand] to the current process manager. - void addCommands(Iterable commands) { - commands.forEach(addCommand); - } - - final Map _fakeRunningProcesses = {}; - - /// Whether this fake has more [FakeCommand]s that are expected to run. - /// - /// This is always `true` for [FakeProcessManager.any]. - bool get hasRemainingExpectations; - - /// The expected [FakeCommand]s that have not yet run. - List get _remainingExpectations; - - @protected - FakeCommand findCommand( - List command, - String? workingDirectory, - Map? environment, - Encoding? encoding, - ); - - int _pid = 9999; - - _FakeProcess _runCommand( - List command, - String? workingDirectory, - Map? environment, - Encoding? encoding, - ) { - _pid += 1; - final FakeCommand fakeCommand = findCommand(command, workingDirectory, environment, encoding); - if (fakeCommand.exception != null) { - assert(fakeCommand.exception is Exception || fakeCommand.exception is Error); - throw fakeCommand.exception!; // ignore: only_throw_errors - } - if (fakeCommand.onRun != null) { - fakeCommand.onRun!(); - } - return _FakeProcess( - fakeCommand.exitCode, - fakeCommand.duration, - _pid, - fakeCommand.stderr, - fakeCommand.stdin, - fakeCommand.stdout, - fakeCommand.completer, - fakeCommand.outputFollowsExit, - ); - } - - @override - Future start( - List command, { - String? workingDirectory, - Map? environment, - bool includeParentEnvironment = true, // ignored - bool runInShell = false, // ignored - io.ProcessStartMode mode = io.ProcessStartMode.normal, // ignored - }) { - final _FakeProcess process = _runCommand(command.cast(), workingDirectory, environment, io.systemEncoding); - if (process._completer != null) { - _fakeRunningProcesses[process.pid] = process; - process.exitCode.whenComplete(() { - _fakeRunningProcesses.remove(process.pid); - }); - } - return Future.value(process); - } - - @override - Future run( - List command, { - String? workingDirectory, - Map? environment, - bool includeParentEnvironment = true, // ignored - bool runInShell = false, // ignored - Encoding? stdoutEncoding = io.systemEncoding, - Encoding? stderrEncoding = io.systemEncoding, - }) async { - final _FakeProcess process = _runCommand(command.cast(), workingDirectory, environment, stdoutEncoding); - await process.exitCode; - return io.ProcessResult( - process.pid, - process._exitCode, - stdoutEncoding == null ? process.stdout : await stdoutEncoding.decodeStream(process.stdout), - stderrEncoding == null ? process.stderr : await stderrEncoding.decodeStream(process.stderr), - ); - } - - @override - io.ProcessResult runSync( - List command, { - String? workingDirectory, - Map? environment, - bool includeParentEnvironment = true, // ignored - bool runInShell = false, // ignored - Encoding? stdoutEncoding = io.systemEncoding, // actual encoder is ignored - Encoding? stderrEncoding = io.systemEncoding, // actual encoder is ignored - }) { - final _FakeProcess process = _runCommand(command.cast(), workingDirectory, environment, stdoutEncoding); - return io.ProcessResult( - process.pid, - process._exitCode, - stdoutEncoding == null ? utf8.encode(process._stdout) : process._stdout, - stderrEncoding == null ? utf8.encode(process._stderr) : process._stderr, - ); - } - - /// Returns false if executable in [excludedExecutables]. - @override - bool canRun(dynamic executable, {String? workingDirectory}) => !excludedExecutables.contains(executable); - - Set excludedExecutables = {}; - - @override - bool killPid(int pid, [io.ProcessSignal signal = io.ProcessSignal.sigterm]) { - // Killing a fake process has no effect unless it has an attached completer. - final _FakeProcess? fakeProcess = _fakeRunningProcesses[pid]; - if (fakeProcess == null) { - return false; - } - if (fakeProcess._completer != null) { - fakeProcess._completer!.complete(); - } - return true; - } -} - -class _FakeAnyProcessManager extends FakeProcessManager { - _FakeAnyProcessManager() : super._(); - - @override - FakeCommand findCommand( - List command, - String? workingDirectory, - Map? environment, - Encoding? encoding, - ) { - return FakeCommand( - command: command, - workingDirectory: workingDirectory, - environment: environment, - encoding: encoding, - ); - } - - @override - void addCommand(FakeCommand command) {} - - @override - bool get hasRemainingExpectations => true; - - @override - List get _remainingExpectations => []; -} - -class _SequenceProcessManager extends FakeProcessManager { - _SequenceProcessManager(this._commands) : super._(); - - final List _commands; - - @override - FakeCommand findCommand( - List command, - String? workingDirectory, - Map? environment, - Encoding? encoding, - ) { - expect( - _commands, - isNotEmpty, - reason: 'ProcessManager was told to execute $command (in $workingDirectory) ' - 'but the FakeProcessManager.list expected no more processes.', - ); - _commands.first._matches(command, workingDirectory, environment, encoding); - return _commands.removeAt(0); - } - - @override - void addCommand(FakeCommand command) { - _commands.add(command); - } - - @override - bool get hasRemainingExpectations => _commands.isNotEmpty; - - @override - List get _remainingExpectations => _commands; -} - -/// Matcher that successfully matches against a [FakeProcessManager] with -/// no remaining expectations ([item.hasRemainingExpectations] returns false). -const Matcher hasNoRemainingExpectations = _HasNoRemainingExpectations(); - -class _HasNoRemainingExpectations extends Matcher { - const _HasNoRemainingExpectations(); - - @override - bool matches(dynamic item, Map matchState) => - item is FakeProcessManager && !item.hasRemainingExpectations; - - @override - Description describe(Description description) => - description.add('a fake process manager with no remaining expectations'); - - @override - Description describeMismatch( - dynamic item, - Description description, - Map matchState, - bool verbose, - ) { - final FakeProcessManager fakeProcessManager = item as FakeProcessManager; - return description.add( - 'has remaining expectations:\n${fakeProcessManager._remainingExpectations.map((FakeCommand command) => command.command).join('\n')}', - ); - } -} diff --git a/cipd_packages/codesign/test/verify_test.dart b/cipd_packages/codesign/test/verify_test.dart deleted file mode 100644 index 49d9d226e..000000000 --- a/cipd_packages/codesign/test/verify_test.dart +++ /dev/null @@ -1,162 +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:codesign/verify.dart'; -import 'package:file/file.dart'; -import 'package:file/memory.dart'; -import 'package:logging/logging.dart'; -import 'package:test/test.dart'; - -import 'src/fake_process_manager.dart'; - -void main() { - const String binaryPath = '/path/to/binary'; - - late FakeProcessManager processManager; - late FileSystem fs; - late Logger logger; - late VerificationService service; - late List logs; - - setUp(() { - fs = MemoryFileSystem.test(); - fs.file(binaryPath).createSync(recursive: true); - processManager = FakeProcessManager.empty(); - logs = []; - logger = Logger.detached('test'); - logger.onRecord.listen((LogRecord record) => logs.add(record)); - service = VerificationService( - binaryPath: binaryPath, - fs: fs, - logger: logger, - pm: processManager, - ); - }); - - test('parses codesign output and can present', () async { - processManager.addCommands(const [ - FakeCommand( - command: ['codesign', '--display', '-vv', binaryPath], - stderr: ''' -Executable=$binaryPath -Identifier=mybinary -Format=Mach-O thin (x86_64) -CodeDirectory v=20500 size=38000 flags=0x10000(runtime) hashes=1177+7 location=embedded -Signature size=8979 -Authority=Developer ID Application: Dev Shop ABC (ABCC0VV123) -Authority=Developer ID Certification Authority -Authority=Apple Root CA -Timestamp=Jan 9, 2023 at 9:39:07 AM -Info.plist=not bound -TeamIdentifier=ABCC0VV123 -Runtime Version=13.1.0 -Sealed Resources=none -Internal requirements count=1 size=164 -''', - ), - FakeCommand( - command: [ - 'codesign', - '--verify', - '-v', - '-R=notarized', - '--check-notarization', - binaryPath, - ], - stderr: ''' -$binaryPath: valid on disk -$binaryPath: satisfies its Designated Requirement -$binaryPath: explicit requirement satisfied -''', - ), - ]); - final VerificationResult result = await service.run(); - expect(processManager, hasNoRemainingExpectations); - expect(result, VerificationResult.codesignedAndNotarized); - expect( - service.present(), - ''' -Authority: Developer ID Application: Dev Shop ABC (ABCC0VV123) -Time stamp: Jan 9, 2023 at 9:39:07 AM -Format: Mach-O thin (x86_64) -Signature size: 8979 -Notarization: true -''', - ); - }); - - test('detects codesigned but not notarized binary', () async { - processManager.addCommands(const [ - FakeCommand( - command: ['codesign', '--display', '-vv', binaryPath], - stderr: ''' -Executable=$binaryPath -Identifier=mybinary -Format=Mach-O thin (x86_64) -CodeDirectory v=20500 size=38000 flags=0x10000(runtime) hashes=1177+7 location=embedded -Signature size=8979 -Authority=Developer ID Application: Dev Shop ABC (ABCC0VV123) -Authority=Developer ID Certification Authority -Authority=Apple Root CA -Timestamp=Jan 9, 2023 at 9:39:07 AM -Info.plist=not bound -TeamIdentifier=ABCC0VV123 -Runtime Version=13.1.0 -Sealed Resources=none -Internal requirements count=1 size=164 -''', - ), - FakeCommand( - command: [ - 'codesign', - '--verify', - '-v', - '-R=notarized', - '--check-notarization', - binaryPath, - ], - stderr: ''' -$binaryPath: valid on disk -$binaryPath: satisfies its Designated Requirement -test-requirement: code failed to satisfy specified code requirement(s) -''', - exitCode: 3, - ), - ]); - final VerificationResult result = await service.run(); - expect(processManager, hasNoRemainingExpectations); - expect(result, VerificationResult.codesignedOnly); - expect( - service.present(), - ''' -Authority: Developer ID Application: Dev Shop ABC (ABCC0VV123) -Time stamp: Jan 9, 2023 at 9:39:07 AM -Format: Mach-O thin (x86_64) -Signature size: 8979 -Notarization: false -''', - ); - }); - - test('detects unsigned binary', () async { - processManager.addCommands(const [ - FakeCommand( - command: ['codesign', '--display', '-vv', binaryPath], - stderr: '$binaryPath: code object is not signed at all', - exitCode: 1, - ), - ]); - final VerificationResult result = await service.run(); - expect(processManager, hasNoRemainingExpectations); - expect(result, VerificationResult.unsigned); - expect( - logs.first, - isA().having( - (LogRecord record) => record.message, - 'message', - contains('File $binaryPath is not codesigned.'), - ), - ); - }); -} diff --git a/cipd_packages/codesign/tool/build.sh b/cipd_packages/codesign/tool/build.sh deleted file mode 100755 index fe8f01016..000000000 --- a/cipd_packages/codesign/tool/build.sh +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env bash -# 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. - -# Fetches corresponding dart sdk from CIPD for different platforms, builds -# an executable binary of codesign to `build` folder. -# -# This build script will be triggered on Mac code signing machines. - -set -e - -command -v cipd > /dev/null || { - echo "Please install CIPD (available from depot_tools) and add to path first."; - exit -1; -} - -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd)" -OS="`uname`" - -cipd ensure -ensure-file $DIR/ensure_file -root $DIR - -pushd $DIR/.. - -if [[ -d "build" ]]; then - echo "Please remove the build directory before proceeding" - exit -1 -fi - -mkdir -p build -tool/bin/dart pub get -tool/bin/dart compile exe bin/codesign.dart -o build/codesign - -cp -f LICENSE build/ - -popd diff --git a/cipd_packages/codesign/tool/ensure_file b/cipd_packages/codesign/tool/ensure_file deleted file mode 100644 index a6985f82f..000000000 --- a/cipd_packages/codesign/tool/ensure_file +++ /dev/null @@ -1,3 +0,0 @@ -$ServiceURL https://chrome-infra-packages.appspot.com/ - -dart/dart-sdk/${os}-${arch} beta diff --git a/cipd_packages/device_doctor/.gitignore b/cipd_packages/device_doctor/.gitignore deleted file mode 100644 index c206c4511..000000000 --- a/cipd_packages/device_doctor/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -# CIPD and Dart-SDK binaries -.cipd/ -dart-sdk/ - -# Build outputs -.dart_tool/ -build/ -.ssh/ - -# Dart -.packages diff --git a/cipd_packages/device_doctor/LICENSE b/cipd_packages/device_doctor/LICENSE deleted file mode 100644 index d5384ca42..000000000 --- a/cipd_packages/device_doctor/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/cipd_packages/device_doctor/README.md b/cipd_packages/device_doctor/README.md deleted file mode 100644 index 5311ecdfe..000000000 --- a/cipd_packages/device_doctor/README.md +++ /dev/null @@ -1,89 +0,0 @@ -# device_doctor - -This utility tool is used by LUCI infrastructure to manage device health -for Flutter LUCI swarming bots. - -It offers support for different host platforms: linux, mac and windows, and -different devices: android and iOS. - -## Dependencies - -Different devices require different tools to be in the `path` beforehand. - -### android -* `adb` - -### iOS -* `idevice_id` -* `idevicediagnostics` -* `xcrun` - -## Building - -This tool is meant to be published as an -[AOT compiled binary](https://chrome-infra-packages.appspot.com/p/flutter/device_doctor) -distributed via CIPD. - -Build the tool for different host platforms on corresponding machines. It will -automatically download a suitable version of Dart to build the binary. - -To create the CIPD package, make sure that the `build/` folder does not exist. - -### Auto build - -Every new commit will trigger pre-submit builders to auto build a new version -for different platforms without any tag/ref. - -When a new commit is submitted, post-submit builders will trigger a new version -with a tag of `commit_sha`, and a ref of `latest`. - -### Manual build - -Running `tool/build.sh` or `tool/build.bat` will build an executable binary in -the `build` folder. Then push to cipd by running - -```bash -cipd create -in build \ - -name flutter/device_doctor/-amd64 \ - -ref \ - -tag sha_timestamp:_ -``` - -* os: `linux`, `mac`, or `windows`. -* ref: `release` or `staging` - -## How to use - -`device_doctor` is the executable binary, and can be called - -```bash -/path/to/device_doctor --action --device-os -``` - -Use `/path/to/device_doctor --help` to learn more. - -**Note**: this tool is assuming one connected device on each host, but can be easily extended -to support multiple devices. - -## Releasing device_doctor - -The release process of the tool is controlled by setting `release` and `staging` refs. - -To promote a given version to staging you can run the following command: - -``` -cipd set-ref flutter/device_doctor/- -ref staging -version -``` - -Example for mac and amd64: - -``` -cipd set-ref flutter/device_doctor/mac-amd64 -ref staging -version IQgKjNstWbFhUuMVp898zZoPKRd66KLRKuiY88XYQXAC -``` - -After extensive validation on staging you can promote the package to release using: - - -``` -cipd set-ref flutter/device_doctor/- -ref release -version -``` diff --git a/cipd_packages/device_doctor/bin/ios_debug_symbol_doctor.dart b/cipd_packages/device_doctor/bin/ios_debug_symbol_doctor.dart deleted file mode 100644 index aed5f763e..000000000 --- a/cipd_packages/device_doctor/bin/ios_debug_symbol_doctor.dart +++ /dev/null @@ -1,32 +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. - -/// This is not a part of the main Device Doctor because it depends on Xcode -/// being installed and set up in the environment, while the main iOS Device -/// Doctor workflow runs before Xcode is provisioned. - -import 'dart:io'; - -import 'package:args/command_runner.dart'; -import 'package:device_doctor/device_doctor.dart'; -import 'package:logging/logging.dart'; - -const String helpFlag = 'help'; - -Future main(List args) async { - // Write logs to stdout - Logger.root.onRecord.listen((LogRecord record) { - stdout.writeln(record.toString()); - }); - - final CommandRunner runner = CommandRunner( - 'ios-debug-symbol-doctor', - 'Tool for diagnosing and recovering from iOS debug symbols not synched with the host by Xcode', - ) - ..addCommand(DiagnoseCommand()) - ..addCommand(RecoverCommand()); - - final bool? success = await runner.run(args); - exit(success == true ? 0 : 1); -} diff --git a/cipd_packages/device_doctor/bin/main.dart b/cipd_packages/device_doctor/bin/main.dart deleted file mode 100644 index a8beff11a..000000000 --- a/cipd_packages/device_doctor/bin/main.dart +++ /dev/null @@ -1,90 +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:io'; - -import 'package:args/args.dart'; - -import 'package:device_doctor/device_doctor.dart'; - -const String actionFlag = 'action'; -const String deviceOSFlag = 'device-os'; -const String helpFlag = 'help'; -const String outputFlag = 'output'; -const List supportedOptions = ['healthcheck', 'prepare', 'recovery', 'properties']; -const List supportedDeviceOS = ['ios', 'android']; -const String defaultOutputPath = '.output'; - -/// These values will be initialized in `_checkArgs` function, -/// and used in `main` function. -String? _action; -String? _deviceOS; -File? _output; - -/// Manage `healthcheck`, `prepare, `recovery`, and `properties` for devices. -/// -/// For `healthcheck`, if no device is found or any health check fails an stderr will be logged, -/// and an exception will be thrown. -/// -/// For `recovery`, it will do cleanup, reboot, etc. to try bringing device back to a working state. -/// -/// For `prepare`, it will prepare the device before running tasks, like kill running processes, etc. -/// -/// For `properties`, it will return device properties/dimensions, like manufacture, base_buildid, etc. -/// -/// Usage: -/// dart main.dart --action --deviceOS -Future main(List args) async { - final ArgParser parser = ArgParser(); - parser - ..addFlag( - '$helpFlag', - help: 'Prints usage info.', - abbr: 'h', - callback: (bool value) { - if (value) { - stdout.write('${parser.usage}\n'); - exit(1); - } - }, - ) - ..addOption( - '$actionFlag', - help: 'Supported actions.', - allowed: supportedOptions, - allowedHelp: { - 'healthcheck': 'Check device health status.', - 'recovery': 'Clean up and reboot device.', - 'properties': 'Return device properties/dimensions.', - }, - ) - ..addOption('$outputFlag', help: 'Path to the output file') - ..addOption( - '$deviceOSFlag', - help: 'Supported device OS.', - allowed: supportedDeviceOS, - allowedHelp: {'android': 'Available for linux, mac, and windows.', 'ios': 'Available for mac.'}, - ); - - final ArgResults argResults = parser.parse(args); - _action = argResults[actionFlag]; - _deviceOS = argResults[deviceOSFlag]; - _output = File(argResults[outputFlag] ?? defaultOutputPath); - - final DeviceDiscovery deviceDiscovery = DeviceDiscovery(_deviceOS, _output); - - switch (_action) { - case 'healthcheck': - await deviceDiscovery.checkDevices(); - break; - case 'prepare': - await deviceDiscovery.prepareDevices(); - break; - case 'recovery': - await deviceDiscovery.recoverDevices(); - break; - case 'properties': - await deviceDiscovery.deviceProperties(); - } -} diff --git a/cipd_packages/device_doctor/lib/device_doctor.dart b/cipd_packages/device_doctor/lib/device_doctor.dart deleted file mode 100644 index 82aac9c05..000000000 --- a/cipd_packages/device_doctor/lib/device_doctor.dart +++ /dev/null @@ -1,10 +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. - -export 'src/device.dart'; -export 'src/health.dart'; -export 'src/host_utils.dart'; -export 'src/ios_device.dart'; -export 'src/utils.dart'; -export 'src/ios_debug_symbol_doctor.dart'; diff --git a/cipd_packages/device_doctor/lib/src/android_device.dart b/cipd_packages/device_doctor/lib/src/android_device.dart deleted file mode 100644 index 8d5062b51..000000000 --- a/cipd_packages/device_doctor/lib/src/android_device.dart +++ /dev/null @@ -1,505 +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:meta/meta.dart'; -import 'package:process/process.dart'; -import 'package:retry/retry.dart'; - -import 'device.dart'; -import 'health.dart'; -import 'host_utils.dart'; -import 'mac.dart'; -import 'utils.dart'; - -// The minimum battery level to run a task with a scale of 100%. -const int _kBatteryMinLevel = 15; -// The maximum battery temprature to run a task with a Celsius degree. -const int _kBatteryMaxTemperatureInCelsius = 34; - -class AndroidDeviceDiscovery implements DeviceDiscovery { - factory AndroidDeviceDiscovery(File? output) { - return _instance ??= AndroidDeviceDiscovery._(output); - } - - final File? _outputFilePath; - AndroidDeviceDiscovery._(this._outputFilePath); - - @visibleForTesting - AndroidDeviceDiscovery.testing(this._outputFilePath); - - // Parses information about a device. Example: - // - // 015d172c98400a03 device usb:340787200X product:nakasi model:Nexus_7 device:grouper - static final RegExp _kDeviceRegex = RegExp(r'^(\S+)\s+(\S+)(.*)'); - - static AndroidDeviceDiscovery? _instance; - - Future _deviceListOutput(Duration timeout, {ProcessManager? processManager}) async { - return eval('adb', ['devices', '-l'], canFail: false, processManager: processManager).timeout(timeout); - } - - Future> _deviceListOutputWithRetries(Duration retryDuration, {ProcessManager? processManager}) async { - const Duration deviceOutputTimeout = Duration(seconds: 15); - final RetryOptions r = RetryOptions( - maxAttempts: 3, - delayFactor: retryDuration, - ); - return r.retry( - () async { - final String result = await _deviceListOutput(deviceOutputTimeout, processManager: processManager); - return result.trim().split('\n'); - }, - retryIf: (Exception e) => e is TimeoutException, - onRetry: (Exception e) => _killAdbServer(processManager: processManager), - ); - } - - void _killAdbServer({ProcessManager? processManager}) async { - if (Platform.isWindows) { - await killAllRunningProcessesOnWindows('adb', processManager: processManager); - } else { - await eval('adb', ['kill-server'], canFail: false, processManager: processManager); - } - } - - @override - Future> discoverDevices({ - Duration retryDuration = const Duration(seconds: 10), - ProcessManager? processManager, - }) async { - processManager ??= LocalProcessManager(); - final List output = await _deviceListOutputWithRetries(retryDuration, processManager: processManager); - final List results = []; - for (String line in output) { - // Skip lines like: * daemon started successfully * - if (line.startsWith('* daemon ')) continue; - - if (line.startsWith('List of devices')) continue; - - if (_kDeviceRegex.hasMatch(line)) { - final Match? match = _kDeviceRegex.firstMatch(line); - - final String? deviceID = match?[1]; - final String? deviceState = match?[2]; - - if (!const ['unauthorized', 'offline'].contains(deviceState)) { - results.add(deviceID!); - } - } else { - throw 'Failed to parse device from adb output: $line'; - } - } - return results.map((String id) => AndroidDevice(deviceId: id)).toList(); - } - - @override - Future>> checkDevices({ProcessManager? processManager}) async { - processManager ??= LocalProcessManager(); - final List defaultChecks = []; - defaultChecks.add(await killAdbServerCheck(processManager: processManager)); - final Map> results = >{}; - for (AndroidDevice device in await discoverDevices(processManager: processManager)) { - final List checks = defaultChecks; - checks.add(HealthCheckResult.success(kDeviceAccessCheckKey)); - checks.add(await adbPowerServiceCheck(processManager: processManager)); - checks.add(await developerModeCheck(processManager: processManager)); - checks.add(await screenOnCheck(processManager: processManager)); - checks.add(await screenSaverCheck(processManager: processManager)); - checks.add(await screenRotationCheck(processManager: processManager)); - checks.add(await batteryLevelCheck(processManager: processManager)); - checks.add(await batteryTemperatureCheck(processManager: processManager)); - if (Platform.isMacOS) { - checks.add(await userAutoLoginCheck(processManager: processManager)); - } - results['android-device-${device.deviceId}'] = checks; - } - final Map> healthCheckMap = await healthcheck(results); - writeToFile(json.encode(healthCheckMap), _outputFilePath!); - return results; - } - - /// Checks and returns the device properties, like manufacturer, base_buildid, etc. - /// - /// It supports multiple devices, but here we are assuming only one device is attached. - @override - Future> deviceProperties({ProcessManager? processManager}) async { - final List devices = await discoverDevices(processManager: processManager); - Map properties = {}; - if (devices.isEmpty) { - writeToFile(json.encode(properties), _outputFilePath!); - stdout.writeln('No devices available.'); - return properties; - } - properties = await getDeviceProperties(devices[0], processManager: processManager); - final String propertiesJson = json.encode(properties); - - writeToFile(propertiesJson, _outputFilePath!); - stdout.writeln('Properties for deviceID ${devices[0].deviceId}: $propertiesJson'); - return properties; - } - - /// Gets android device properties based on swarming bot configuration. - /// - /// Refer function `get_dimensions` from - /// https://source.chromium.org/chromium/infra/infra/+/master:luci/appengine/swarming/swarming_bot/api/platforms/android.py - Future> getDeviceProperties(AndroidDevice device, {ProcessManager? processManager}) async { - processManager ??= LocalProcessManager(); - final Map deviceProperties = {}; - final Map propertyMap = {}; - LineSplitter.split( - await eval('adb', ['-s', device.deviceId!, 'shell', 'getprop'], processManager: processManager), - ).forEach((String property) { - final List propertyList = property.replaceAll('[', '').replaceAll(']', '').split(': '); - - /// Deal with entries spanning only one line. - /// - /// This is to skip unused entries spanninning multiple lines. - /// For example: - /// [persist.sys.boot.reason.history]: [reboot,ota,1613677289 - /// reboot,userrequested,1613677269 - /// reboot,userrequested,1613508544] - if (propertyList.length == 2) { - propertyMap[propertyList[0].trim()] = propertyList[1].trim(); - } - }); - - deviceProperties['product_brand'] = propertyMap['ro.product.brand']!; - deviceProperties['build_id'] = propertyMap['ro.build.id']!; - deviceProperties['build_type'] = propertyMap['ro.build.type']!; - deviceProperties['product_model'] = propertyMap['ro.product.model']!; - deviceProperties['product_board'] = propertyMap['ro.product.board']!; - return deviceProperties; - } - - @override - Future recoverDevices() async { - for (Device device in await discoverDevices()) { - await device.recover(); - } - } - - @visibleForTesting - Future adbPowerServiceCheck({ProcessManager? processManager}) async { - HealthCheckResult healthCheckResult; - try { - await eval('adb', ['shell', 'dumpsys', 'power'], processManager: processManager); - healthCheckResult = HealthCheckResult.success(kAdbPowerServiceCheckKey); - } on BuildFailedError catch (error) { - healthCheckResult = HealthCheckResult.failure(kAdbPowerServiceCheckKey, error.toString()); - } - return healthCheckResult; - } - - @visibleForTesting - - /// The health check for Android device screen on. - /// - /// An Android device screen is on when both `mHoldingWakeLockSuspendBlocker` and - /// `mHoldingDisplaySuspendBlocker` are true. - Future screenOnCheck({ProcessManager? processManager}) async { - HealthCheckResult healthCheckResult; - try { - final String result = await eval( - 'adb', - ['shell', 'dumpsys', 'power', '|', 'grep', 'mHoldingDisplaySuspendBlocker'], - processManager: processManager, - ); - if (result.trim() == 'mHoldingDisplaySuspendBlocker=true') { - healthCheckResult = HealthCheckResult.success(kScreenOnCheckKey); - } else { - healthCheckResult = HealthCheckResult.failure(kScreenOnCheckKey, 'screen is off'); - } - } on BuildFailedError catch (error) { - healthCheckResult = HealthCheckResult.failure(kScreenOnCheckKey, error.toString()); - } - return healthCheckResult; - } - - @visibleForTesting - - /// The health check for Android device adb kill server. - /// - /// Kill adb server before running any health check to avoid device quarantine: - /// https://github.com/flutter/flutter/issues/93075. - Future killAdbServerCheck({ProcessManager? processManager}) async { - HealthCheckResult healthCheckResult; - try { - await eval('adb', ['kill-server'], processManager: processManager); - healthCheckResult = HealthCheckResult.success(kKillAdbServerCheckKey); - } on BuildFailedError catch (error) { - healthCheckResult = HealthCheckResult.failure(kKillAdbServerCheckKey, error.toString()); - } - return healthCheckResult; - } - - @visibleForTesting - - /// The health check for Android device developer mode. - /// - /// Developer mode `on` is expected for a healthy Android device. - Future developerModeCheck({ProcessManager? processManager}) async { - HealthCheckResult healthCheckResult; - try { - final String result = await eval( - 'adb', - ['shell', 'settings', 'get', 'global', 'development_settings_enabled'], - processManager: processManager, - ); - // The output of `development_settings_enabled` is `1` when developer mode is on. - if (result == '1') { - healthCheckResult = HealthCheckResult.success(kDeveloperModeCheckKey); - } else { - healthCheckResult = HealthCheckResult.failure(kDeveloperModeCheckKey, 'developer mode is off'); - } - } on BuildFailedError catch (error) { - healthCheckResult = HealthCheckResult.failure(kDeveloperModeCheckKey, error.toString()); - } - return healthCheckResult; - } - - /// The health check to validate screen rotation is off. - /// - /// Screen rotation is expected disabled for a healthy Android device. - Future screenRotationCheck({ProcessManager? processManager}) async { - HealthCheckResult healthCheckResult; - try { - final String result = await eval( - 'adb', - ['shell', 'settings', 'get', 'system', 'accelerometer_rotation'], - processManager: processManager, - ); - // The output of `screensaver_enabled` is `0` when screensaver mode is off. - if (result == '0') { - healthCheckResult = HealthCheckResult.success(kScreenRotationCheckKey); - } else { - healthCheckResult = HealthCheckResult.failure(kScreenRotationCheckKey, 'Screen rotation is enabled'); - } - } on BuildFailedError catch (error) { - healthCheckResult = HealthCheckResult.failure(kScreenRotationCheckKey, error.toString()); - } - return healthCheckResult; - } - - /// The health check to validate screensaver is off. - /// - /// Screensaver`off` is expected for a healthy Android device. - Future screenSaverCheck({ProcessManager? processManager}) async { - HealthCheckResult healthCheckResult; - try { - final String result = await eval( - 'adb', - ['shell', 'settings', 'get', 'secure', 'screensaver_enabled'], - processManager: processManager, - ); - // The output of `screensaver_enabled` is `0` when screensaver mode is off. - if (result == '0') { - healthCheckResult = HealthCheckResult.success(kScreenSaverCheckKey); - } else { - healthCheckResult = HealthCheckResult.failure(kScreenSaverCheckKey, 'Screensaver is on'); - } - } on BuildFailedError catch (error) { - healthCheckResult = HealthCheckResult.failure(kScreenSaverCheckKey, error.toString()); - } - return healthCheckResult; - } - - /// The health check for battery level. - Future batteryLevelCheck({ProcessManager? processManager}) async { - HealthCheckResult healthCheckResult; - try { - // The battery level returns two rows. For example: - // level: 100 - // mod level: -1 - final String levelResults = await eval( - 'adb', - ['shell', 'dumpsys', 'battery', '|', 'grep', 'level'], - processManager: processManager, - ); - final RegExp levelRegExp = RegExp('level: (?.+)'); - final RegExpMatch? match = levelRegExp.firstMatch(levelResults); - final int level = int.parse(match!.namedGroup('level')!); - if (level < _kBatteryMinLevel) { - healthCheckResult = - HealthCheckResult.failure(kBatteryLevelCheckKey, 'Battery level ($level) is below $_kBatteryMinLevel'); - } else { - healthCheckResult = HealthCheckResult.success(kBatteryLevelCheckKey); - } - } on BuildFailedError catch (error) { - healthCheckResult = HealthCheckResult.failure(kScreenSaverCheckKey, error.toString()); - } - return healthCheckResult; - } - - /// The health check for battery temperature. - Future batteryTemperatureCheck({ProcessManager? processManager}) async { - HealthCheckResult healthCheckResult; - try { - // The battery temperature returns one row. For example: - // temperature: 240 - // It means 24°C. - final String tempResult = await eval( - 'adb', - ['shell', 'dumpsys', 'battery', '|', 'grep', 'temperature'], - processManager: processManager, - ); - final RegExp? tempRegExp = RegExp('temperature: (?.+)'); - final RegExpMatch match = tempRegExp!.firstMatch(tempResult)!; - final int temperature = int.parse(match.namedGroup('temperature')!); - if (temperature > _kBatteryMaxTemperatureInCelsius * 10) { - healthCheckResult = HealthCheckResult.failure( - kBatteryTemperatureCheckKey, - 'Battery temperature (${(temperature * 0.1).toInt()}°C) is over $_kBatteryMaxTemperatureInCelsius°C', - ); - } else { - healthCheckResult = HealthCheckResult.success(kBatteryTemperatureCheckKey); - } - } on BuildFailedError catch (error) { - healthCheckResult = HealthCheckResult.failure(kBatteryTemperatureCheckKey, error.toString()); - } - return healthCheckResult; - } - - @override - Future prepareDevices() async { - for (Device device in await discoverDevices()) { - await device.prepare(); - } - } -} - -class AndroidDevice implements Device { - AndroidDevice({@required this.deviceId}); - - @override - final String? deviceId; - - @override - Future recover() async { - await cleanDevice(); - } - - @visibleForTesting - Future deletePackageCache() async { - final ProcessResult result = Process.runSync('adb', ['shell', 'pm', 'list', 'packages']); - - if (result.exitCode != 0) { - throw Exception(result.stderr as String); - } - final packages = []; - - // Listen to the stdout and stderr streams - LineSplitter().convert(result.stdout).forEach((data) { - final packageMatch = RegExp(r'package:(.+)$').firstMatch(data); - if (packageMatch != null) { - final packageName = packageMatch.group(1); - if (packageName != null) { - packages.add(packageName); - } - } - }); - for (final p in packages) { - final r = Process.runSync('adb', ['shell', 'pm', 'clear', p]); - if (r.exitCode != 0) { - print('Clearing package $p resulted in a non-zero exit.'); - print('STDERR: ${r.stderr}'); - print('STDOUT: ${r.stdout}'); - } else { - print('Uninstalling package $p : STDOUT(${r.stdout})'); - } - } - } - - @visibleForTesting - Future delete3Ppackages() async { - final ProcessResult result = Process.runSync('adb', ['shell', 'pm', 'list', 'packages', '-3']); - if (result.exitCode != 0) { - throw Exception(result.stderr as String); - } - final packages = []; - - // Listen to the stdout and stderr streams - LineSplitter().convert(result.stdout).forEach((data) { - final packageMatch = RegExp(r'package:(.+)$').firstMatch(data); - if (packageMatch != null) { - final packageName = packageMatch.group(1); - if (packageName != null) { - packages.add(packageName); - } - } - }); - for (final p in packages) { - final r = Process.runSync('adb', ['shell', 'pm', 'uninstall', p]); - if (r.exitCode != 0) { - print('Uninstalling package $p resulted in a non-zero exit.'); - print('STDERR: ${r.stderr}'); - print('STDOUT: ${r.stdout}'); - } else { - print('Uninstalling package $p : STDOUT(${r.stdout})'); - } - } - } - - @visibleForTesting - Future cleanDevice({ProcessManager? processManager}) async { - processManager ??= LocalProcessManager(); - final int timeoutSecs = 60; - print('Device recovery: deleting package caches...'); - await eval('adb', ['wait-for-device'], canFail: false, processManager: processManager) - .timeout(Duration(seconds: timeoutSecs)); - await deletePackageCache(); - await eval('adb', ['wait-for-device'], canFail: false, processManager: processManager) - .timeout(Duration(seconds: timeoutSecs)); - print('Device recovery: deleting 3P packages...'); - await delete3Ppackages(); - await eval('adb', ['wait-for-device'], canFail: false, processManager: processManager) - .timeout(Duration(seconds: timeoutSecs)); - print('Device recovery: rebooting...'); - await eval('adb', ['reboot'], canFail: false, processManager: processManager); - return true; - } - - @override - Future prepare() async { - await killProcesses(); - } - - /// Kill top running process if existing. - @visibleForTesting - Future killProcesses({ProcessManager? processManager}) async { - processManager ??= LocalProcessManager(); - String result; - result = await eval( - 'adb', - ['shell', 'dumpsys', 'activity', '|', 'grep', 'top-activity'], - canFail: true, - processManager: processManager, - ); - - // Skip uninstalling process when no device is available or no application exists. - if (result == 'adb: no devices/emulators found' || result.isEmpty) { - stdout.write('no process is running'); - return true; - } - final List results = result.trim().split('\n'); - // Example result: - // - // Proc # 0: fore T/A/T trm: 0 4544:com.google.android.googlequicksearchbox/u0a66 (top-activity) - final List processes = - results.map((result) => result.substring(result.lastIndexOf(':') + 1, result.lastIndexOf('/'))).toList(); - try { - for (String process in processes) { - await eval('adb', ['shell', 'am', 'force-stop', process], processManager: processManager); - stdout.write('adb stop process: $process'); - } - } on BuildFailedError catch (error) { - stderr.write('uninstall applications fails: $error'); - return false; - } - return true; - } -} diff --git a/cipd_packages/device_doctor/lib/src/device.dart b/cipd_packages/device_doctor/lib/src/device.dart deleted file mode 100644 index 99ef4c484..000000000 --- a/cipd_packages/device_doctor/lib/src/device.dart +++ /dev/null @@ -1,55 +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:io'; - -import 'package:process/process.dart'; - -import 'android_device.dart'; -import 'health.dart'; -import 'ios_device.dart'; - -/// Discovers available devices and chooses one to work with. -abstract class DeviceDiscovery { - factory DeviceDiscovery(String? deviceOs, File? output) { - switch (deviceOs) { - case 'ios': - return IosDeviceDiscovery(output); - case 'android': - return AndroidDeviceDiscovery(output); - default: - throw StateError('Unsupported device operating system: $deviceOs'); - } - } - - /// Lists all available devices' IDs. - Future> discoverDevices({Duration retryDuration = const Duration(seconds: 10)}); - - /// Checks the health of the available devices. - Future>> checkDevices({ProcessManager processManager}); - - /// Checks and returns the device properties, like manufacturer, base_buildid, etc. - /// - /// Currently it supports only android devices, but can extend to iOS devices. - Future> deviceProperties({ProcessManager processManager}); - - /// Recovers the device. - Future recoverDevices(); - - /// Prepares the device. - Future prepareDevices(); -} - -/// A proxy for one specific phone device. -abstract class Device { - /// A unique device identifier. - String? get deviceId; - - /// Recovers the device back to a healthy state. - Future recover(); - - /// Prepares the device before running tasks. - Future prepare(); -} diff --git a/cipd_packages/device_doctor/lib/src/health.dart b/cipd_packages/device_doctor/lib/src/health.dart deleted file mode 100644 index bb78ae362..000000000 --- a/cipd_packages/device_doctor/lib/src/health.dart +++ /dev/null @@ -1,102 +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:io'; - -import 'package:path/path.dart' as path; -import 'package:platform/platform.dart' as platform; -import 'package:process/process.dart'; - -import 'utils.dart'; - -/// Closes system dialogs on iOS, e.g. the one about new system update. -/// -/// The dialogs often cause test flakiness and performance regressions. -Future closeIosDialog({ - ProcessManager pm = const LocalProcessManager(), - String? deviceId, - platform.Platform pl = const platform.LocalPlatform(), - String infraDialog = 'infra-dialog', -}) async { - Directory dialogDir = dir(path.dirname(Platform.script.path), 'tool', infraDialog); - if (!await dialogDir.exists()) { - dialogDir = dir(Directory.current.path, 'tool', infraDialog); - if (!await dialogDir.exists()) { - fail('Unable to find infra-dialog at $dialogDir'); - } - } - - // Runs the single XCUITest in infra-dialog. - await inDirectory(dialogDir, () async { - final List command = - 'xcrun xcodebuild -project infra-dialog.xcodeproj -scheme infra-dialog -destination -quiet id=$deviceId test' - .split(' '); - // By default the above command relies on automatic code signing, while on devicelab machines - // it should utilize manual code signing as that is more stable. Below overwrites the code - // signing config if one exists in the environment. - if (pl.environment['FLUTTER_XCODE_CODE_SIGN_STYLE'] != null) { - command.add("CODE_SIGN_STYLE=${pl.environment['FLUTTER_XCODE_CODE_SIGN_STYLE']}"); - command.add("DEVELOPMENT_TEAM=${pl.environment['FLUTTER_XCODE_DEVELOPMENT_TEAM']}"); - command.add("PROVISIONING_PROFILE_SPECIFIER=${pl.environment['FLUTTER_XCODE_PROVISIONING_PROFILE_SPECIFIER']}"); - } - final Process proc = await pm.start(command, workingDirectory: dialogDir.path); - final int exitCode = await proc.exitCode; - if (exitCode != 0) { - fail('Command "$command" failed with exit code $exitCode.'); - } - }); - return HealthCheckResult.success('close iOS dialog'); -} - -/// Result of a health check for a specific parameter. -class HealthCheckResult { - HealthCheckResult.success(this.name, [this.details]) : succeeded = true; - HealthCheckResult.failure(this.name, this.details) : succeeded = false; - HealthCheckResult.error(this.name, dynamic error, dynamic stackTrace) - : succeeded = false, - details = 'ERROR: $error\n${stackTrace ?? ''}'; - - final String name; - final bool succeeded; - final String? details; - - @override - String toString() { - final StringBuffer buf = StringBuffer(name); - buf.writeln(succeeded ? 'succeeded' : 'failed'); - if (details != null && details!.trim().isNotEmpty) { - buf.writeln(); - // Indent details by 4 spaces - for (String line in details!.trim().split('\n')) { - buf.writeln(' $line'); - } - } - return '$buf'; - } -} - -/// Check healthiness for discovered devices. -Future>> healthcheck(Map> deviceChecks) async { - final Map> healthcheckMap = >{}; - if (deviceChecks.isEmpty) { - healthcheckMap[kAttachedDeviceHealthcheckKey] = { - 'status': false, - 'details': kAttachedDeviceHealthcheckValue, - }; - } else { - healthcheckMap[kAttachedDeviceHealthcheckKey] = {'status': true, 'details': null}; - } - for (String deviceID in deviceChecks.keys) { - final List checks = deviceChecks[deviceID]!; - for (HealthCheckResult healthCheckResult in checks) { - final Map healthCheckResultMap = { - 'status': healthCheckResult.succeeded, - 'details': healthCheckResult.details, - }; - healthcheckMap[healthCheckResult.name] = healthCheckResultMap; - } - } - return healthcheckMap; -} diff --git a/cipd_packages/device_doctor/lib/src/host_utils.dart b/cipd_packages/device_doctor/lib/src/host_utils.dart deleted file mode 100644 index b51006bc7..000000000 --- a/cipd_packages/device_doctor/lib/src/host_utils.dart +++ /dev/null @@ -1,60 +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:io'; - -import 'package:process/process.dart'; - -final RegExp _whitespace = RegExp(r'\s+'); - -/// Kill all running processes on windows. -/// -/// This is used when we first fail to list devices and need a retry. -Future killAllRunningProcessesOnWindows(String processName, {ProcessManager? processManager}) async { - processManager ??= LocalProcessManager(); - // Avoid endless loop when a process from a different use exists, and fails - // to get killed every try. - int tries = 3; - while (tries > 0) { - tries--; - final pids = runningProcessesOnWindows(processName, processManager: processManager); - if (pids.isEmpty) { - return true; - } - for (String pid in pids) { - processManager.runSync(['taskkill', '/pid', pid, '/f']); - } - // Killed processes don't release resources instantenously. - const Duration delay = Duration(seconds: 1); - await Future.delayed(delay); - } - return false; -} - -List runningProcessesOnWindows(String processName, {ProcessManager? processManager}) { - processManager ??= LocalProcessManager(); - final ProcessResult result = processManager.runSync(['powershell', 'Get-CimInstance', 'Win32_Process']); - final List pids = []; - if (result.exitCode == 0) { - final String stdoutResult = result.stdout as String; - for (String rawProcess in stdoutResult.split('\n')) { - final String process = rawProcess.trim(); - if (!process.contains(processName)) { - continue; - } - final List parts = process.split(_whitespace); - - final String processPid = parts[0]; - final String currentRunningProcessPid = pid.toString(); - // Don't kill current process - if (processPid == currentRunningProcessPid) { - continue; - } - pids.add(processPid); - } - } - return pids; -} diff --git a/cipd_packages/device_doctor/lib/src/ios_debug_symbol_doctor.dart b/cipd_packages/device_doctor/lib/src/ios_debug_symbol_doctor.dart deleted file mode 100644 index 32d2b0804..000000000 --- a/cipd_packages/device_doctor/lib/src/ios_debug_symbol_doctor.dart +++ /dev/null @@ -1,253 +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' as io; - -import 'package:args/command_runner.dart'; -import 'package:file/file.dart'; -import 'package:file/local.dart'; -import 'package:process/process.dart'; -import 'package:logging/logging.dart'; -import 'package:platform/platform.dart'; - -class DiagnoseCommand extends Command { - DiagnoseCommand({ - this.processManager = const LocalProcessManager(), - Logger? loggerOverride, - }) : logger = loggerOverride ?? Logger.root; - - final Logger logger; - - final ProcessManager processManager; - - final String name = 'diagnose'; - final String description = 'Diagnose whether attached iOS devices have errors.'; - - Future run() async { - final List command = ['xcrun', 'xcdevice', 'list']; - final io.ProcessResult result = await processManager.run( - command, - ); - if (result.exitCode != 0) { - logger.severe( - '$command failed with exit code ${result.exitCode}\n${result.stderr}', - ); - return false; - } - final String stdout = result.stdout as String; - logger.info(stdout); - final Iterable devices = XCDevice.parseJson(stdout); - final Iterable devicesWithErrors = devices.where((XCDevice device) => device.hasError); - - if (devicesWithErrors.isNotEmpty) { - logger.severe('Found devices with errors!'); - - for (final XCDevice device in devicesWithErrors) { - logger.severe('${device.name}: ${device.error}'); - } - return false; - } - - return true; - } -} - -class RecoverCommand extends Command { - RecoverCommand({ - this.processManager = const LocalProcessManager(), - Logger? loggerOverride, - this.fs = const LocalFileSystem(), - this.platform = const LocalPlatform(), - }) : logger = loggerOverride ?? Logger.root { - argParser - ..addOption( - 'cocoon-root', - help: 'Path to the root of the Cocoon repo. This is used to find the Build dashboard macos project, which is ' - 'then used to open Xcode.', - mandatory: true, - ) - ..addOption( - 'timeout', - help: 'Integer number of seconds to allow Xcode to run before killing it.', - defaultsTo: '300', - ); - } - - final Logger logger; - final ProcessManager processManager; - final FileSystem fs; - final Platform platform; - - final String name = 'recover'; - final String description = 'Open Xcode UI to allow it to sync debug symbols from the iPhone'; - - /// Xcode Project workspace file for the build dashboard Flutter app. - /// - /// Should be located at //cocoon/dashboard/ios/Runner.xcodeproj/project.xcworkspace. - Directory get dashboardXcWorkspace { - final String cocoonRootPath = argResults!['cocoon-root']; - final Directory cocoonRoot = fs.directory(cocoonRootPath); - final Directory dashboardXcWorkspace = cocoonRoot - .childDirectory('dashboard') - .childDirectory('ios') - .childDirectory('Runner.xcodeproj') - .childDirectory('project.xcworkspace') - .absolute; - if (!dashboardXcWorkspace.existsSync()) { - throw StateError( - 'You provided the --cocoon-root option with "$cocoonRootPath", and the device doctor tried to ' - "locate the build dashboard's project.xcworkspace directory at \"${dashboardXcWorkspace.path}\" " - 'but that path does not exist on disk.', - ); - } - return dashboardXcWorkspace; - } - - @override - Future run() async { - final int? timeoutSeconds = int.tryParse(argResults!['timeout']); - if (timeoutSeconds == null) { - throw ArgumentError('Could not parse an integer from the option --timeout="${argResults!['timeout']}"'); - } - - _deleteSymbols(); - - // Prompt Xcode to first setup without opening the app. - // This will return very quickly if there is no work to do. - logger.info('Running Xcode first launch...'); - final io.ProcessResult runFirstLaunchResult = await processManager.run([ - 'xcrun', - 'xcodebuild', - '-runFirstLaunch', - ]); - final String runFirstLaunchStdout = runFirstLaunchResult.stdout.trim(); - if (runFirstLaunchStdout.isNotEmpty) { - logger.info('stdout from `xcodebuild -runFirstLaunch`:\n$runFirstLaunchStdout\n'); - } - final String runFirstLaunchStderr = runFirstLaunchResult.stderr.trim(); - if (runFirstLaunchStderr.isNotEmpty) { - logger.info('stderr from `xcodebuild -runFirstLaunch`:\n$runFirstLaunchStderr\n'); - } - final int runFirstLaunchCode = runFirstLaunchResult.exitCode; - if (runFirstLaunchCode != 0) { - logger.info('Failed running `xcodebuild -runFirstLaunch` with code $runFirstLaunchCode!'); - return false; - } - - final Duration timeout = Duration(seconds: timeoutSeconds); - logger.info('Launching Xcode...'); - final Future xcodeFuture = processManager.run([ - 'open', - '-n', // Opens a new instance of the application even if one is already running - '-F', // Opens the application "fresh," without restoring windows - '-W', // Wait for the opened application (Xcode) to close - dashboardXcWorkspace.path, - ]); - - unawaited( - xcodeFuture.then((io.ProcessResult result) { - logger.info('Open closed...'); - final String stdout = result.stdout.trim(); - if (stdout.isNotEmpty) { - logger.info('stdout from `open`:\n$stdout\n'); - } - final String stderr = result.stderr.trim(); - if (stderr.isNotEmpty) { - logger.info('stderr from `open`:\n$stderr\n'); - } - if (result.exitCode != 0) { - throw Exception('Failed opening Xcode!'); - } - }), - ); - - logger.info('Waiting for $timeoutSeconds seconds'); - await Future.delayed(timeout); - logger.info('Waited for $timeoutSeconds seconds, now killing Xcode'); - final io.ProcessResult result = await processManager.run(['killall', '-9', 'Xcode']); - - if (result.exitCode != 0) { - logger.severe('Failed killing Xcode!'); - return false; - } - return true; - } - - /// Delete all symbols by deleting the `iOS DeviceSupport` directory. - /// Xcode will regenerate this folder and symbols for connected devices - /// when Xcode is opened. - void _deleteSymbols() { - final String? home = platform.environment['HOME']; - if (home == null) { - logger.warning('\$HOME path was not found'); - return; - } - final Directory deviceSupportDirectory = fs.directory('$home/Library/Developer/Xcode/iOS DeviceSupport'); - if (!deviceSupportDirectory.existsSync()) { - logger.warning('iOS Device Support directory was not found at ${deviceSupportDirectory.path}'); - return; - } - logger.info('Deleting iOS DeviceSupport...'); - try { - deviceSupportDirectory.deleteSync(recursive: true); - } on FileSystemException catch (e) { - // Error that indicates why files cannot be deleted, such as - // another program having the files open. - logger.severe('${e.message}: ${e.osError}'); - } - } -} - -/// A Device configuration as output by `xcrun xcdevice list`. -/// -/// As more fields are needed, they can be added to this class. It is -/// recommended to make all fields nullable in case a different version of Xcode -/// does not implement it. -class XCDevice { - const XCDevice._({ - required this.error, - required this.name, - }); - - static const String _debugSymbolDescriptionPattern = r' is busy: Fetching debug symbols for '; - static final RegExp _preparingPhoneForDevelopmentPattern = RegExp( - r'Preparing .* for development\. Xcode will continue when .* is finished\.', - ); - - /// Parse subset of JSON from `parseJson` associated with a particular XCDevice. - factory XCDevice.fromMap(Map map) { - final Map? error = map['error'] as Map?; - // We should only specifically pattern match on known fatal errors, and - // ignore the rest. - bool validError = false; - if (error != null) { - final String description = error['description'] as String; - if (description.contains(_debugSymbolDescriptionPattern) || - _preparingPhoneForDevelopmentPattern.hasMatch(description)) { - validError = true; - } else { - print('not matching pattern: $description'); - } - } - return XCDevice._( - error: validError ? error : null, - name: map['name'] as String, - ); - } - - final Map? error; - final String name; - - bool get hasError => error != null; - - /// Parse the complete output of `xcrun xcdevice list`. - static Iterable parseJson(String jsonString) { - final List devices = json.decode(jsonString) as List; - return devices.map((Object? obj) { - return XCDevice.fromMap(obj as Map); - }); - } -} diff --git a/cipd_packages/device_doctor/lib/src/ios_device.dart b/cipd_packages/device_doctor/lib/src/ios_device.dart deleted file mode 100644 index 524f5c337..000000000 --- a/cipd_packages/device_doctor/lib/src/ios_device.dart +++ /dev/null @@ -1,285 +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' show LineSplitter, json; -import 'dart:io'; - -import 'package:meta/meta.dart'; -import 'package:process/process.dart'; - -import 'device.dart'; -import 'health.dart'; -import 'mac.dart'; -import 'utils.dart'; - -/// The minimum battery level to run a task with a scale of 100%. -const int _kBatteryMinLevel = 15; - -/// Identifiers for devices that should never be rebooted. -final Set noRebootList = { - '822ef7958bba573829d85eef4df6cbdd86593730', // 32bit iPhone requires manual intervention on reboot. -}; - -/// IOS implementation of [DeviceDiscovery]. -/// -/// Discovers available ios devices and chooses one to work with. -class IosDeviceDiscovery implements DeviceDiscovery { - factory IosDeviceDiscovery(File? output) { - return _instance ??= IosDeviceDiscovery._(output); - } - - final File? _outputFilePath; - - IosDeviceDiscovery._(this._outputFilePath); - - @visibleForTesting - IosDeviceDiscovery.testing(this._outputFilePath); - - static IosDeviceDiscovery? _instance; - - @override - Future> discoverDevices({Duration retryDuration = const Duration(seconds: 10)}) async { - final List discoveredDevices = - LineSplitter.split(await deviceListOutput()).map((String id) => IosDevice(deviceId: id)).toList(); - stdout.write('ios devices discovered: ${discoveredDevices.map((e) => e.deviceId).toList()}'); - return discoveredDevices; - } - - Future deviceListOutput({ - ProcessManager processManager = const LocalProcessManager(), - }) async { - final String fullPathIdeviceId = await getMacBinaryPath('idevice_id', processManager: processManager); - stdout.write('idevice_id path $fullPathIdeviceId'); - return eval(fullPathIdeviceId, ['-l'], processManager: processManager); - } - - @override - Future>> checkDevices({ProcessManager? processManager}) async { - processManager ??= LocalProcessManager(); - final Map> results = >{}; - for (Device device in await discoverDevices()) { - final List checks = []; - checks.add(HealthCheckResult.success(kDeviceAccessCheckKey)); - checks.add(await keychainUnlockCheck(processManager: processManager)); - checks.add(await certCheck(processManager: processManager)); - checks.add(await devicePairCheck(processManager: processManager)); - checks.add(await userAutoLoginCheck(processManager: processManager)); - checks.add(await deviceProvisioningProfileCheck(device.deviceId, processManager: processManager)); - checks.add(await batteryLevelCheck(processManager: processManager)); - results['ios-device-${device.deviceId}'] = checks; - } - final Map> healthCheckMap = await healthcheck(results); - writeToFile(json.encode(healthCheckMap), _outputFilePath!); - return results; - } - - /// Checks and returns the device properties. - @override - Future> deviceProperties({ProcessManager? processManager}) async { - return {}; - } - - @override - Future recoverDevices() async { - for (Device device in await discoverDevices()) { - await device.recover(); - } - } - - @visibleForTesting - Future deviceProvisioningProfileCheck(String? deviceId, {ProcessManager? processManager}) async { - HealthCheckResult healthCheckResult; - try { - final String? homeDir = Platform.environment['HOME']; - - final String out = await eval( - 'ls', - ['$homeDir/Library/MobileDevice/Provisioning\ Profiles'], - processManager: processManager, - ); - // Split filenames - final profiles = LineSplitter.split(out).toList(); - - // Check all provisioning profiles in the directory to - // to see if any contain a valid profile - bool validProfileFound = false; - for (var file in profiles) { - final String provisionFileContent = await eval( - 'security', - ['cms', '-D', '-i', '$homeDir/Library/MobileDevice/Provisioning\ Profiles/$file'], - processManager: processManager, - ); - if (provisionFileContent.contains(deviceId!)) { - validProfileFound = true; - break; - } - } - // If any file contained a valid profile, then set result accordingly - if (validProfileFound) { - healthCheckResult = HealthCheckResult.success(kDeviceProvisioningProfileCheckKey); - } else { - healthCheckResult = HealthCheckResult.failure( - kDeviceProvisioningProfileCheckKey, - 'device does not exist in the provisioning profile', - ); - } - } on BuildFailedError catch (error) { - healthCheckResult = HealthCheckResult.failure(kDeviceProvisioningProfileCheckKey, error.toString()); - } - return healthCheckResult; - } - - @visibleForTesting - Future keychainUnlockCheck({ProcessManager? processManager}) async { - HealthCheckResult healthCheckResult; - try { - await eval(kUnlockLoginKeychain, [], processManager: processManager); - healthCheckResult = HealthCheckResult.success(kKeychainUnlockCheckKey); - } on BuildFailedError catch (error) { - healthCheckResult = HealthCheckResult.failure(kKeychainUnlockCheckKey, error.toString()); - } - return healthCheckResult; - } - - @visibleForTesting - Future batteryLevelCheck({ProcessManager? processManager}) async { - HealthCheckResult healthCheckResult; - try { - final String batteryCheckResult = await eval( - 'ideviceinfo', - ['-q', 'com.apple.mobile.battery', '-k', 'BatteryCurrentCapacity'], - processManager: processManager, - ); - final int level = int.parse(batteryCheckResult.isEmpty ? '0' : batteryCheckResult); - if (level < _kBatteryMinLevel) { - healthCheckResult = - HealthCheckResult.failure(kBatteryLevelCheckKey, 'Battery level ($level) is below $_kBatteryMinLevel'); - } else { - healthCheckResult = HealthCheckResult.success(kBatteryLevelCheckKey); - } - } on BuildFailedError catch (error) { - healthCheckResult = HealthCheckResult.failure(kBatteryLevelCheckKey, error.toString()); - } - return healthCheckResult; - } - - @visibleForTesting - Future certCheck({ProcessManager? processManager}) async { - HealthCheckResult healthCheckResult; - try { - final String certCheckResult = - await eval('security', ['find-identity', '-p', 'codesigning', '-v'], processManager: processManager); - if (certCheckResult.contains('Apple Development: Flutter Devicelab') && - certCheckResult.contains('1 valid identities found') && - !certCheckResult.contains('CSSMERR_TP_CERT_REVOKED')) { - healthCheckResult = HealthCheckResult.success(kCertCheckKey); - } else { - healthCheckResult = HealthCheckResult.failure(kCertCheckKey, certCheckResult); - } - } on BuildFailedError catch (error) { - healthCheckResult = HealthCheckResult.failure(kCertCheckKey, error.toString()); - } - return healthCheckResult; - } - - @visibleForTesting - Future devicePairCheck({ProcessManager? processManager}) async { - HealthCheckResult healthCheckResult; - try { - final String devicePairCheckResult = - await eval('idevicepair', ['validate'], processManager: processManager); - if (devicePairCheckResult.contains('SUCCESS')) { - healthCheckResult = HealthCheckResult.success(kDevicePairCheckKey); - } else { - healthCheckResult = HealthCheckResult.failure(kDevicePairCheckKey, devicePairCheckResult); - } - } on BuildFailedError catch (error) { - healthCheckResult = HealthCheckResult.failure(kDevicePairCheckKey, error.toString()); - } - return healthCheckResult; - } - - @override - Future prepareDevices() async { - for (Device device in await discoverDevices()) { - await device.prepare(); - } - } -} - -/// iOS device. -class IosDevice implements Device { - const IosDevice({@required this.deviceId}); - - @override - final String? deviceId; - - @override - Future recover() async { - await uninstall_applications(); - await restart_device(); - } - - @override - Future prepare() async { - return; - } - - /// Restart iOS device. - @visibleForTesting - Future restart_device({ProcessManager? processManager}) async { - processManager ??= LocalProcessManager(); - try { - if (noRebootList.contains(deviceId)) { - stdout.write('Device not marked for reboot.'); - return true; - } - final String fullPathIdevicediagnostics = - await getMacBinaryPath('idevicediagnostics', processManager: processManager); - await eval(fullPathIdevicediagnostics, ['restart'], processManager: processManager); - } on BuildFailedError catch (error) { - stderr.write('device restart fails: $error'); - return false; - } - stdout.write('Restart device complete.'); - return true; - } - - /// Uninstall applications from a device. - /// - /// This is to prevent application installation failure caused by using different signing - /// certificate from previous installed application. - /// Issue: https://github.com/flutter/flutter/issues/76896 - @visibleForTesting - Future uninstall_applications({ProcessManager? processManager}) async { - processManager ??= LocalProcessManager(); - String result; - final String fullPathIdeviceInstaller = await getMacBinaryPath('ideviceinstaller', processManager: processManager); - try { - result = await eval(fullPathIdeviceInstaller, ['-l'], processManager: processManager); - } on BuildFailedError catch (error) { - stderr.write('list applications fails: $error'); - return false; - } - - // Skip uninstalling process when no device is available or no application exists. - if (result == 'No device found.' || result == 'CFBundleIdentifier, CFBundleVersion, CFBundleDisplayName') { - stdout.write('No device was found or no application to uninstall exists.'); - return true; - } - final List results = result.trim().split('\n'); - final List bundleIdentifiers = results.sublist(1).map((e) => e.split(',')[0].trim()).toList(); - try { - for (String bundleIdentifier in bundleIdentifiers) { - await eval(fullPathIdeviceInstaller, ['-U', bundleIdentifier], processManager: processManager); - } - } on BuildFailedError catch (error) { - stderr.write('uninstall applications fails: $error'); - return false; - } - stdout.write('Uninstall complete.'); - return true; - } -} diff --git a/cipd_packages/device_doctor/lib/src/mac.dart b/cipd_packages/device_doctor/lib/src/mac.dart deleted file mode 100644 index 655530988..000000000 --- a/cipd_packages/device_doctor/lib/src/mac.dart +++ /dev/null @@ -1,35 +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:process/process.dart'; - -import 'health.dart'; -import 'utils.dart'; - -/// The health check for Mac swarming user auto login. -/// -/// The `swarming` user auto login is required for Flutter desktop tests which need to run -/// GUI application on Mac. -Future userAutoLoginCheck({ProcessManager? processManager}) async { - HealthCheckResult healthCheckResult; - try { - final String user = await eval( - 'defaults', - ['read', '/Library/Preferences/com.apple.loginwindow', 'autoLoginUser'], - processManager: processManager, - ); - // User `swarming` is expected setup for Mac bot auto login. - if (user == 'swarming') { - healthCheckResult = HealthCheckResult.success(kUserAutoLoginCheckKey); - } else { - healthCheckResult = - HealthCheckResult.failure(kUserAutoLoginCheckKey, 'swarming user is not setup for auto login'); - } - } on BuildFailedError catch (error) { - healthCheckResult = HealthCheckResult.failure(kUserAutoLoginCheckKey, error.toString()); - } - return healthCheckResult; -} diff --git a/cipd_packages/device_doctor/lib/src/utils.dart b/cipd_packages/device_doctor/lib/src/utils.dart deleted file mode 100644 index 416c4f042..000000000 --- a/cipd_packages/device_doctor/lib/src/utils.dart +++ /dev/null @@ -1,167 +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:io'; - -import 'package:meta/meta.dart'; -import 'package:path/path.dart' as path; -import 'package:process/process.dart'; - -import 'dart:convert' show utf8; - -const String kDeviceAccessCheckKey = 'device_access'; -const String kAttachedDeviceHealthcheckKey = 'attached_device'; -const String kAttachedDeviceHealthcheckValue = 'No device is available'; -const String kAdbPowerServiceCheckKey = 'adb_power_service'; -const String kDeveloperModeCheckKey = 'developer_mode'; -const String kScreenOnCheckKey = 'screen_on'; -const String kKillAdbServerCheckKey = 'kill_adb_server'; -const String kKeychainUnlockCheckKey = 'keychain_unlock'; -const String kDeviceProvisioningProfileCheckKey = 'device_provisioning_profile'; -const String kUserAutoLoginCheckKey = 'swarming_user_auto_login'; -const String kUnlockLoginKeychain = '/usr/local/bin/unlock_login_keychain.sh'; -const String kCertCheckKey = 'codesigning_cert'; -const String kDevicePairCheckKey = 'device_pair'; -const String kScreenSaverCheckKey = 'screensaver'; -const String kScreenRotationCheckKey = 'screen_rotation'; -const String kBatteryLevelCheckKey = 'battery_level'; -const String kBatteryTemperatureCheckKey = 'battery_temperature'; -const List kM1BrewBinPaths = ['/opt/homebrew/bin', '/usr/local/bin']; - -void fail(String message) { - throw BuildFailedError(message); -} - -class BuildFailedError extends Error { - BuildFailedError(this.message); - - final String message; - - @override - String toString() => message; -} - -/// Creates a directory from the given path, or multiple path parts by joining -/// them using OS-specific file path separator. -Directory dir( - String thePath, [ - String? part2, - String? part3, - String? part4, - String? part5, - String? part6, - String? part7, - String? part8, -]) { - return Directory(path.join(thePath, part2, part3, part4, part5, part6, part7, part8)); -} - -Future inDirectory(dynamic directory, Future action()) async { - final String previousCwd = path.current; - try { - cd(directory); - return await action(); - } finally { - cd(previousCwd); - } -} - -void cd(dynamic directory) { - Directory d; - if (directory is String) { - d = dir(directory); - } else if (directory is Directory) { - d = directory; - } else { - throw 'Unsupported type ${directory.runtimeType} of $directory'; - } - - if (!d.existsSync()) throw 'Cannot cd into directory that does not exist: $directory'; -} - -/// Starts a process for an executable command, and returns the processes. -Future startProcess( - String executable, - List arguments, { - Map? env, - bool silent = false, - ProcessManager? processManager = const LocalProcessManager(), -}) async { - late Process proc; - try { - proc = await processManager! - .start([executable]..addAll(arguments), environment: env, workingDirectory: path.current); - } catch (error) { - fail(error.toString()); - } - return proc; -} - -/// Executes a command and returns its standard output as a String. -/// -/// Standard error is redirected to the current process' standard error stream. -Future eval( - String executable, - List arguments, { - Map? env, - bool canFail = false, - bool silent = false, - ProcessManager? processManager = const LocalProcessManager(), -}) async { - final Process proc = - await startProcess(executable, arguments, env: env, silent: silent, processManager: processManager); - proc.stderr.listen((List data) { - stderr.add(data); - }); - final String output = await utf8.decodeStream(proc.stdout); - final int exitCode = await proc.exitCode; - - if (exitCode != 0 && !canFail) fail('Executable $executable failed with exit code $exitCode.'); - - return output.trimRight(); -} - -/// Splits [from] into lines and selects those that contain [pattern]. -Iterable grep(Pattern pattern, {@required String? from}) { - return from!.split('\n').where((String line) { - return line.contains(pattern); - }); -} - -/// Write [results] to [filePath]. -void writeToFile(String results, File file) { - if (file.existsSync()) { - try { - file.deleteSync(); - } on FileSystemException catch (error) { - print('Failed to delete ${file.path}: $error'); - } - } - file - ..createSync() - ..writeAsStringSync(results); - return; -} - -/// Return Mac binary path. -/// -/// For M1 bots, binaries like `ideviceinstaller` are installed under `kM1BrewBinPath`, -/// where they are not visible in `$PATH` by default. -Future getMacBinaryPath( - String name, { - ProcessManager processManager = const LocalProcessManager(), -}) async { - final Map env = Map.of(Platform.environment); - String? path = env['PATH'] ?? ''; - final String additionalPaths = kM1BrewBinPaths.join(':'); - path = '$path:$additionalPaths'; - env['PATH'] = path; - final String binaryPath = - await eval('which', [name], canFail: true, processManager: processManager, env: env); - // Throws exception when the binary doesn't exist in either location. - if (binaryPath.isEmpty) { - fail('$name not found.'); - } - return binaryPath; -} diff --git a/cipd_packages/device_doctor/pubspec.yaml b/cipd_packages/device_doctor/pubspec.yaml deleted file mode 100644 index 79e3fcf06..000000000 --- a/cipd_packages/device_doctor/pubspec.yaml +++ /dev/null @@ -1,22 +0,0 @@ -name: device_doctor -description: A Dart tool to check device healthiness and implement recovery mechanisms to get the device back to a working state. -version: 1.0.1 - -environment: - sdk: ">=2.18.0 <4.0.0" - -dependencies: - analyzer: 5.13.0 - args: 2.4.2 - logging: 1.2.0 - meta: 1.11.0 - path: 1.8.3 - process: 5.0.1 - retry: 3.1.2 - yaml: 3.1.2 - -dev_dependencies: - build_runner: 2.4.6 - fake_async: 1.3.1 - mockito: 5.4.2 - test: 1.24.9 diff --git a/cipd_packages/device_doctor/test/src/android_device_test.dart b/cipd_packages/device_doctor/test/src/android_device_test.dart deleted file mode 100644 index 4d36f535f..000000000 --- a/cipd_packages/device_doctor/test/src/android_device_test.dart +++ /dev/null @@ -1,573 +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:file/src/backends/memory/memory_file_system.dart'; -import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; - -import 'package:device_doctor/src/android_device.dart'; -import 'package:device_doctor/src/device.dart'; -import 'package:device_doctor/src/health.dart'; -import 'package:device_doctor/src/utils.dart'; - -import 'utils.dart'; - -void main() { - group('AndroidDeviceDiscovery', () { - late AndroidDeviceDiscovery deviceDiscovery; - late MockProcessManager processManager; - List> output; - Process process; - - setUp(() { - deviceDiscovery = AndroidDeviceDiscovery(MemoryFileSystem().file('output')); - processManager = MockProcessManager(); - }); - - test('deviceDiscovery no retries', () async { - final StringBuffer sb = StringBuffer(); - sb.writeln('List of devices attached'); - sb.writeln('ZY223JQNMR device'); - output = >[utf8.encode(sb.toString())]; - process = FakeProcess(0, out: output); - when(processManager.start(any, workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(process)); - - final List devices = await deviceDiscovery.discoverDevices( - retryDuration: const Duration(seconds: 0), - processManager: processManager, - ); - expect(devices.length, equals(1)); - expect(devices[0].deviceId, equals('ZY223JQNMR')); - }); - - test('deviceDiscovery fails', () async { - when(processManager.start(any, workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => throw TimeoutException('test')); - expect( - deviceDiscovery.discoverDevices(retryDuration: const Duration(seconds: 0), processManager: processManager), - throwsA(TypeMatcher()), - ); - }); - }); - - group('AndroidDeviceProperties', () { - late AndroidDeviceDiscovery deviceDiscovery; - late MockProcessManager processManager; - Process property_process; - Process process; - String output; - - setUp(() { - deviceDiscovery = AndroidDeviceDiscovery(MemoryFileSystem().file('output')); - processManager = MockProcessManager(); - }); - - test('returns empty when no device is attached', () async { - output = 'List of devices attached'; - process = FakeProcess(0, out: >[utf8.encode(output)]); - - when(processManager.start(['adb', 'devices', '-l'], workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(process)); - - expect(await deviceDiscovery.deviceProperties(processManager: processManager), equals({})); - }); - - test('get device properties', () async { - output = '''[ro.product.brand]: [abc] - [ro.build.id]: [def] - [ro.build.type]: [ghi] - [ro.product.model]: [jkl] - [ro.product.board]: [mno] - '''; - property_process = FakeProcess(0, out: >[utf8.encode(output)]); - - when( - processManager.start( - ['adb', '-s', 'ZY223JQNMR', 'shell', 'getprop'], - workingDirectory: anyNamed('workingDirectory'), - ), - ).thenAnswer((_) => Future.value(property_process)); - - final Map deviceProperties = await deviceDiscovery - .getDeviceProperties(AndroidDevice(deviceId: 'ZY223JQNMR'), processManager: processManager); - - const Map expectedProperties = { - 'product_brand': 'abc', - 'build_id': 'def', - 'build_type': 'ghi', - 'product_model': 'jkl', - 'product_board': 'mno', - }; - expect(deviceProperties, equals(expectedProperties)); - }); - }); - - group('AndroidAdbPowerServiceCheck', () { - late AndroidDeviceDiscovery deviceDiscovery; - late MockProcessManager processManager; - Process process; - - setUp(() { - deviceDiscovery = AndroidDeviceDiscovery(MemoryFileSystem().file('output')); - processManager = MockProcessManager(); - }); - - test('returns success when adb power service is available', () async { - process = FakeProcess(0); - when( - processManager - .start(['adb', 'shell', 'dumpsys', 'power'], workingDirectory: anyNamed('workingDirectory')), - ).thenAnswer((_) => Future.value(process)); - - final HealthCheckResult healthCheckResult = - await deviceDiscovery.adbPowerServiceCheck(processManager: processManager); - expect(healthCheckResult.succeeded, true); - expect(healthCheckResult.name, kAdbPowerServiceCheckKey); - }); - - test('returns failure when adb returns none 0 code', () async { - process = FakeProcess(1); - when( - processManager - .start(['adb', 'shell', 'dumpsys', 'power'], workingDirectory: anyNamed('workingDirectory')), - ).thenAnswer((_) => Future.value(process)); - - final HealthCheckResult healthCheckResult = - await deviceDiscovery.adbPowerServiceCheck(processManager: processManager); - expect(healthCheckResult.succeeded, false); - expect(healthCheckResult.name, kAdbPowerServiceCheckKey); - expect(healthCheckResult.details, 'Executable adb failed with exit code 1.'); - }); - }); - - group('AndroidDevloperModeCheck', () { - late AndroidDeviceDiscovery deviceDiscovery; - late MockProcessManager processManager; - Process process; - List> output; - - setUp(() { - deviceDiscovery = AndroidDeviceDiscovery(MemoryFileSystem().file('output')); - processManager = MockProcessManager(); - }); - - test('returns success when developer mode is on', () async { - output = >[utf8.encode('1')]; - process = FakeProcess(0, out: output); - when( - processManager.start( - ['adb', 'shell', 'settings', 'get', 'global', 'development_settings_enabled'], - workingDirectory: anyNamed('workingDirectory'), - ), - ).thenAnswer((_) => Future.value(process)); - - final HealthCheckResult healthCheckResult = - await deviceDiscovery.developerModeCheck(processManager: processManager); - expect(healthCheckResult.succeeded, true); - expect(healthCheckResult.name, kDeveloperModeCheckKey); - }); - - test('returns failure when developer mode is off', () async { - output = >[utf8.encode('0')]; - process = FakeProcess(0, out: output); - when( - processManager.start( - ['adb', 'shell', 'settings', 'get', 'global', 'development_settings_enabled'], - workingDirectory: anyNamed('workingDirectory'), - ), - ).thenAnswer((_) => Future.value(process)); - - final HealthCheckResult healthCheckResult = - await deviceDiscovery.developerModeCheck(processManager: processManager); - expect(healthCheckResult.succeeded, false); - expect(healthCheckResult.name, kDeveloperModeCheckKey); - expect(healthCheckResult.details, 'developer mode is off'); - }); - - test('returns success when screensaver is off', () async { - output = >[utf8.encode('0')]; - process = FakeProcess(0, out: output); - when( - processManager.start( - ['adb', 'shell', 'settings', 'get', 'secure', 'screensaver_enabled'], - workingDirectory: anyNamed('workingDirectory'), - ), - ).thenAnswer((_) => Future.value(process)); - - final HealthCheckResult healthCheckResult = - await deviceDiscovery.screenSaverCheck(processManager: processManager); - expect(healthCheckResult.succeeded, true); - expect(healthCheckResult.name, kScreenSaverCheckKey); - }); - - test('returns failure when screensaver is on', () async { - output = >[utf8.encode('1')]; - process = FakeProcess(0, out: output); - when( - processManager.start( - ['adb', 'shell', 'settings', 'get', 'secure', 'screensaver_enabled'], - workingDirectory: anyNamed('workingDirectory'), - ), - ).thenAnswer((_) => Future.value(process)); - - final HealthCheckResult healthCheckResult = - await deviceDiscovery.screenSaverCheck(processManager: processManager); - expect(healthCheckResult.succeeded, false); - expect(healthCheckResult.name, kScreenSaverCheckKey); - expect(healthCheckResult.details, 'Screensaver is on'); - }); - - test('returns failure when adb return none 0 code', () async { - process = FakeProcess(1); - when( - processManager.start( - ['adb', 'shell', 'settings', 'get', 'global', 'development_settings_enabled'], - workingDirectory: anyNamed('workingDirectory'), - ), - ).thenAnswer((_) => Future.value(process)); - - final HealthCheckResult healthCheckResult = - await deviceDiscovery.developerModeCheck(processManager: processManager); - expect(healthCheckResult.succeeded, false); - expect(healthCheckResult.name, kDeveloperModeCheckKey); - expect(healthCheckResult.details, 'Executable adb failed with exit code 1.'); - }); - }); - - group('AndroidScreenOnCheck', () { - late AndroidDeviceDiscovery deviceDiscovery; - late MockProcessManager processManager; - Process process; - List> output; - - setUp(() { - deviceDiscovery = AndroidDeviceDiscovery(MemoryFileSystem().file('output')); - processManager = MockProcessManager(); - }); - - test('returns success when screen is on', () async { - const String screenMessage = ''' - mHoldingDisplaySuspendBlocker=true - '''; - output = >[utf8.encode(screenMessage)]; - process = FakeProcess(0, out: output); - when( - processManager.start( - ['adb', 'shell', 'dumpsys', 'power', '|', 'grep', 'mHoldingDisplaySuspendBlocker'], - workingDirectory: anyNamed('workingDirectory'), - ), - ).thenAnswer((_) => Future.value(process)); - - final HealthCheckResult healthCheckResult = await deviceDiscovery.screenOnCheck(processManager: processManager); - expect(healthCheckResult.succeeded, true); - expect(healthCheckResult.name, kScreenOnCheckKey); - }); - - test('returns failure when screen is off', () async { - const String screenMessage = ''' - mHoldingDisplaySuspendBlocker=false - '''; - output = >[utf8.encode(screenMessage)]; - process = FakeProcess(0, out: output); - when( - processManager.start( - ['adb', 'shell', 'dumpsys', 'power', '|', 'grep', 'mHoldingDisplaySuspendBlocker'], - workingDirectory: anyNamed('workingDirectory'), - ), - ).thenAnswer((_) => Future.value(process)); - - final HealthCheckResult healthCheckResult = await deviceDiscovery.screenOnCheck(processManager: processManager); - expect(healthCheckResult.succeeded, false); - expect(healthCheckResult.name, kScreenOnCheckKey); - expect(healthCheckResult.details, 'screen is off'); - }); - - test('returns failure when adb return non 0 code', () async { - process = FakeProcess(1); - when( - processManager.start( - ['adb', 'shell', 'dumpsys', 'power', '|', 'grep', 'mHoldingDisplaySuspendBlocker'], - workingDirectory: anyNamed('workingDirectory'), - ), - ).thenAnswer((_) => Future.value(process)); - - final HealthCheckResult healthCheckResult = await deviceDiscovery.screenOnCheck(processManager: processManager); - expect(healthCheckResult.succeeded, false); - expect(healthCheckResult.name, kScreenOnCheckKey); - expect(healthCheckResult.details, 'Executable adb failed with exit code 1.'); - }); - }); - - group('AndroidScreenRotationCheck', () { - late AndroidDeviceDiscovery deviceDiscovery; - late MockProcessManager processManager; - Process process; - List> output; - - setUp(() { - deviceDiscovery = AndroidDeviceDiscovery(MemoryFileSystem().file('output')); - processManager = MockProcessManager(); - }); - - test('returns success when rotation is disabled', () async { - output = >[utf8.encode('0')]; - process = FakeProcess(0, out: output); - when( - processManager.start( - ['adb', 'shell', 'settings', 'get', 'system', 'accelerometer_rotation'], - workingDirectory: anyNamed('workingDirectory'), - ), - ).thenAnswer((_) => Future.value(process)); - - final HealthCheckResult healthCheckResult = - await deviceDiscovery.screenRotationCheck(processManager: processManager); - expect(healthCheckResult.succeeded, true); - expect(healthCheckResult.name, kScreenRotationCheckKey); - }); - - test('returns failure when screen rotation is enabled', () async { - output = >[utf8.encode('1')]; - process = FakeProcess(0, out: output); - when( - processManager.start( - ['adb', 'shell', 'settings', 'get', 'system', 'accelerometer_rotation'], - workingDirectory: anyNamed('workingDirectory'), - ), - ).thenAnswer((_) => Future.value(process)); - - final HealthCheckResult healthCheckResult = - await deviceDiscovery.screenRotationCheck(processManager: processManager); - expect(healthCheckResult.succeeded, false); - expect(healthCheckResult.name, kScreenRotationCheckKey); - expect(healthCheckResult.details, 'Screen rotation is enabled'); - }); - }); - - group('AndroidDeviceKillProcesses', () { - late AndroidDevice device; - late MockProcessManager processManager; - Process listProcess; - Process killProcess; - List>? output; - - setUp(() { - device = AndroidDevice(deviceId: 'abc'); - processManager = MockProcessManager(); - }); - - test('successfully killed running processes', () async { - output = >[ - utf8.encode( - 'Proc #27: fg T/ /TOP LCM t: 0 0:com.google.android.apps.nexuslauncher/u0a199 (top-activity)', - ), - ]; - listProcess = FakeProcess(0, out: output); - killProcess = FakeProcess(0); - when( - processManager.start( - ['adb', 'shell', 'dumpsys', 'activity', '|', 'grep', 'top-activity'], - workingDirectory: anyNamed('workingDirectory'), - ), - ).thenAnswer((_) => Future.value(listProcess)); - when( - processManager.start( - ['adb', 'shell', 'am', 'force-stop', 'com.google.android.apps.nexuslauncher'], - workingDirectory: anyNamed('workingDirectory'), - ), - ).thenAnswer((_) => Future.value(killProcess)); - - final bool result = await device.killProcesses(processManager: processManager); - expect(result, true); - }); - - test('no running processes', () async { - output = >[]; - listProcess = FakeProcess(0, out: output); - killProcess = FakeProcess(0); - when( - processManager.start( - ['adb', 'shell', 'dumpsys', 'activity', '|', 'grep', 'top-activity'], - workingDirectory: anyNamed('workingDirectory'), - ), - ).thenAnswer((_) => Future.value(listProcess)); - when( - processManager.start( - ['adb', 'shell', 'am', 'force-stop', 'com.google.android.apps.nexuslauncher'], - workingDirectory: anyNamed('workingDirectory'), - ), - ).thenAnswer((_) => Future.value(killProcess)); - - final bool result = await device.killProcesses(processManager: processManager); - expect(result, true); - }); - - test('fails to kill running processes', () async { - output = >[ - utf8.encode( - 'Proc #27: fg T/ /TOP LCM t: 0 0:com.google.android.apps.nexuslauncher/u0a199 (top-activity)', - ), - ]; - listProcess = FakeProcess(0, out: output); - killProcess = FakeProcess(1); - when( - processManager.start( - ['adb', 'shell', 'dumpsys', 'activity', '|', 'grep', 'top-activity'], - workingDirectory: anyNamed('workingDirectory'), - ), - ).thenAnswer((_) => Future.value(listProcess)); - when( - processManager.start( - ['adb', 'shell', 'am', 'force-stop', 'com.google.android.apps.nexuslauncher'], - workingDirectory: anyNamed('workingDirectory'), - ), - ).thenAnswer((_) => Future.value(killProcess)); - - final bool result = await device.killProcesses(processManager: processManager); - expect(result, false); - }); - }); - - group('KillAdbServerCheck', () { - late AndroidDeviceDiscovery deviceDiscovery; - late MockProcessManager processManager; - Process process; - - setUp(() { - deviceDiscovery = AndroidDeviceDiscovery(MemoryFileSystem().file('output')); - processManager = MockProcessManager(); - }); - - test('returns success when adb power service is killed', () async { - process = FakeProcess(0); - when(processManager.start(['adb', 'kill-server'], workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(process)); - - final HealthCheckResult healthCheckResult = - await deviceDiscovery.killAdbServerCheck(processManager: processManager); - expect(healthCheckResult.succeeded, true); - expect(healthCheckResult.name, kKillAdbServerCheckKey); - }); - - test('returns failure when adb returns non 0 code', () async { - process = FakeProcess(1); - when(processManager.start(['adb', 'kill-server'], workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(process)); - - final HealthCheckResult healthCheckResult = - await deviceDiscovery.killAdbServerCheck(processManager: processManager); - expect(healthCheckResult.succeeded, false); - expect(healthCheckResult.name, kKillAdbServerCheckKey); - expect(healthCheckResult.details, 'Executable adb failed with exit code 1.'); - }); - }); - - group('BatteryLevelCheck', () { - late AndroidDeviceDiscovery deviceDiscovery; - late MockProcessManager processManager; - Process process; - List> output; - - setUp(() { - deviceDiscovery = AndroidDeviceDiscovery(MemoryFileSystem().file('output')); - processManager = MockProcessManager(); - }); - - test('returns success when battery level is high', () async { - const String screenMessage = ''' - level: 100 - mod level: -1 - '''; - output = >[utf8.encode(screenMessage)]; - process = FakeProcess(0, out: output); - when( - processManager.start( - ['adb', 'shell', 'dumpsys', 'battery', '|', 'grep', 'level'], - workingDirectory: anyNamed('workingDirectory'), - ), - ).thenAnswer((_) => Future.value(process)); - - final HealthCheckResult healthCheckResult = - await deviceDiscovery.batteryLevelCheck(processManager: processManager); - expect(healthCheckResult.succeeded, true); - expect(healthCheckResult.name, kBatteryLevelCheckKey); - }); - - test('returns failure when battery level is below threshold', () async { - const String screenMessage = ''' - level: 10 - mod level: -1 - '''; - output = >[utf8.encode(screenMessage)]; - process = FakeProcess(0, out: output); - when( - processManager.start( - ['adb', 'shell', 'dumpsys', 'battery', '|', 'grep', 'level'], - workingDirectory: anyNamed('workingDirectory'), - ), - ).thenAnswer((_) => Future.value(process)); - - final HealthCheckResult healthCheckResult = - await deviceDiscovery.batteryLevelCheck(processManager: processManager); - expect(healthCheckResult.succeeded, false); - expect(healthCheckResult.name, kBatteryLevelCheckKey); - expect(healthCheckResult.details, 'Battery level (10) is below 15'); - }); - }); - - group('BatteryTemperatureCheck', () { - late AndroidDeviceDiscovery deviceDiscovery; - late MockProcessManager processManager; - Process process; - List> output; - - setUp(() { - deviceDiscovery = AndroidDeviceDiscovery(MemoryFileSystem().file('output')); - processManager = MockProcessManager(); - }); - - test('returns success when battery temperature is low', () async { - const String screenMessage = ''' - temperature: 24 - '''; - output = >[utf8.encode(screenMessage)]; - process = FakeProcess(0, out: output); - when( - processManager.start( - ['adb', 'shell', 'dumpsys', 'battery', '|', 'grep', 'temperature'], - workingDirectory: anyNamed('workingDirectory'), - ), - ).thenAnswer((_) => Future.value(process)); - - final HealthCheckResult healthCheckResult = - await deviceDiscovery.batteryTemperatureCheck(processManager: processManager); - expect(healthCheckResult.succeeded, true); - expect(healthCheckResult.name, kBatteryTemperatureCheckKey); - }); - - test('returns failure when battery temperature is above threshold', () async { - const String screenMessage = ''' - temperature: 350 - '''; - output = >[utf8.encode(screenMessage)]; - process = FakeProcess(0, out: output); - when( - processManager.start( - ['adb', 'shell', 'dumpsys', 'battery', '|', 'grep', 'temperature'], - workingDirectory: anyNamed('workingDirectory'), - ), - ).thenAnswer((_) => Future.value(process)); - - final HealthCheckResult healthCheckResult = - await deviceDiscovery.batteryTemperatureCheck(processManager: processManager); - expect(healthCheckResult.succeeded, false); - expect(healthCheckResult.name, kBatteryTemperatureCheckKey); - expect(healthCheckResult.details, 'Battery temperature (35°C) is over 34°C'); - }); - }); -} diff --git a/cipd_packages/device_doctor/test/src/fake_ios_device.dart b/cipd_packages/device_doctor/test/src/fake_ios_device.dart deleted file mode 100644 index dd7bfcba1..000000000 --- a/cipd_packages/device_doctor/test/src/fake_ios_device.dart +++ /dev/null @@ -1,33 +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:device_doctor/src/ios_device.dart'; -import 'package:process/process.dart'; - -class FakeIosDeviceDiscovery extends IosDeviceDiscovery { - // ignore: use_super_parameters - FakeIosDeviceDiscovery(output) : super.testing(output); - - List? _outputs; - int _pos = 0; - - set outputs(List outputs) { - _pos = 0; - _outputs = outputs; - } - - @override - Future deviceListOutput({ - ProcessManager processManager = const LocalProcessManager(), - }) async { - _pos++; - if (_outputs?[_pos - 1] is String) { - return _outputs?[_pos - 1] as String; - } else { - throw _outputs?[_pos - 1]; - } - } -} diff --git a/cipd_packages/device_doctor/test/src/health_test.dart b/cipd_packages/device_doctor/test/src/health_test.dart deleted file mode 100644 index 91c330dea..000000000 --- a/cipd_packages/device_doctor/test/src/health_test.dart +++ /dev/null @@ -1,112 +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:io'; - -import 'package:mockito/mockito.dart'; -import 'package:platform/platform.dart' as platform; -import 'package:test/test.dart'; - -import 'package:device_doctor/src/health.dart'; -import 'package:device_doctor/src/utils.dart'; - -import 'utils.dart'; - -void main() { - group('testCloseIosDialog', () { - late MockProcessManager pm; - - setUp(() async { - pm = MockProcessManager(); - }); - - test('succeeded', () async { - final Process proc = FakeProcess(0); - when(pm.start(any, workingDirectory: anyNamed('workingDirectory'))).thenAnswer((_) => Future.value(proc)); - - final HealthCheckResult res = await closeIosDialog(pm: pm); - - expect(res.succeeded, isTrue); - }); - - test('succeeded with code signing overwrite', () async { - final Process proc = FakeProcess(0); - when(pm.start(any, workingDirectory: anyNamed('workingDirectory'))).thenAnswer((_) => Future.value(proc)); - final platform.Platform pl = platform.FakePlatform( - environment: { - 'FLUTTER_XCODE_CODE_SIGN_STYLE': 'Manual', - 'FLUTTER_XCODE_DEVELOPMENT_TEAM': 'S8QB4VV633', - 'FLUTTER_XCODE_PROVISIONING_PROFILE_SPECIFIER': 'a name with space', - }, - ); - - final HealthCheckResult res = await closeIosDialog(pm: pm, pl: pl); - - expect(res.succeeded, isTrue); - }); - - test('failed', () async { - final Process proc = FakeProcess(123); - when(pm.start(any, workingDirectory: anyNamed('workingDirectory'))).thenAnswer((_) => Future.value(proc)); - - expect( - closeIosDialog(pm: pm), - throwsA(TypeMatcher()), - ); - }); - - test('tool is not found', () async { - final Process proc = FakeProcess(123); - when(pm.start(any, workingDirectory: anyNamed('workingDirectory'))).thenAnswer((_) => Future.value(proc)); - - expect( - closeIosDialog(pm: pm, infraDialog: 'abc'), - throwsA(TypeMatcher()), - ); - }); - }); - - group('healthCheck', () { - late Map> deviceChecks; - - setUp(() async { - deviceChecks = >{}; - }); - - test('with no device', () async { - final Map> healthcheckMap = await healthcheck(deviceChecks); - expect(healthcheckMap, >{ - kAttachedDeviceHealthcheckKey: {'status': false, 'details': kAttachedDeviceHealthcheckValue}, - }); - }); - - test('with failed check', () async { - final List healthChecks = [ - HealthCheckResult.success('check1'), - HealthCheckResult.failure('check2', 'abc'), - ]; - deviceChecks['device1'] = healthChecks; - final Map> healthcheckMap = await healthcheck(deviceChecks); - expect(healthcheckMap, >{ - kAttachedDeviceHealthcheckKey: {'status': true, 'details': null}, - 'check1': {'status': true, 'details': null}, - 'check2': {'status': false, 'details': 'abc'}, - }); - }); - - test('without failed check', () async { - final List healthChecks = [ - HealthCheckResult.success('check1'), - HealthCheckResult.success('check2'), - ]; - deviceChecks['device1'] = healthChecks; - final Map> healthcheckMap = await healthcheck(deviceChecks); - expect(healthcheckMap, >{ - kAttachedDeviceHealthcheckKey: {'status': true, 'details': null}, - 'check1': {'status': true, 'details': null}, - 'check2': {'status': true, 'details': null}, - }); - }); - }); -} diff --git a/cipd_packages/device_doctor/test/src/host_utils_test.dart b/cipd_packages/device_doctor/test/src/host_utils_test.dart deleted file mode 100644 index 7e29d50d7..000000000 --- a/cipd_packages/device_doctor/test/src/host_utils_test.dart +++ /dev/null @@ -1,81 +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:io'; - -import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; -import 'package:device_doctor/src/host_utils.dart'; - -import 'utils.dart'; - -void main() { - group('runningProcessesOnWindows', () { - late MockProcessManager processManager; - String output; - - setUp(() { - processManager = MockProcessManager(); - }); - - test('when there is a single matched process', () async { - output = '123 abc'; - when(processManager.runSync(['powershell', 'Get-CimInstance', 'Win32_Process'])) - .thenAnswer((_) => ProcessResult(1, 0, output, 'def')); - final List processes = runningProcessesOnWindows('abc', processManager: processManager); - expect(processes, equals(['123'])); - }); - - test('when there are more than one matched process', () async { - output = '123 abc\n 456 abc\n 789 def'; - when(processManager.runSync(['powershell', 'Get-CimInstance', 'Win32_Process'])) - .thenAnswer((_) => ProcessResult(1, 0, output, 'def')); - final List processes = runningProcessesOnWindows('abc', processManager: processManager); - expect(processes, equals(['123', '456'])); - }); - - test('when there is no matched process', () async { - output = '123 def'; - when(processManager.runSync(['powershell', 'Get-CimInstance', 'Win32_Process'])) - .thenAnswer((_) => ProcessResult(1, 0, output, 'def')); - final List processes = runningProcessesOnWindows('abc', processManager: processManager); - expect(processes, equals([])); - }); - }); - - group('killAllRunningProcessesOnWindows', () { - late MockProcessManager processManager; - String output; - String pid; - - setUp(() { - processManager = MockProcessManager(); - }); - - test('when there are unkilled processes', () async { - pid = '123'; - output = '$pid abc'; - when(processManager.runSync(['powershell', 'Get-CimInstance', 'Win32_Process'])) - .thenAnswer((_) => ProcessResult(1, 0, output, 'def')); - when(processManager.runSync(['taskkill', '/pid', pid, '/f'])) - .thenAnswer((_) => ProcessResult(1, 0, 'test', 'test')); - final bool result = await killAllRunningProcessesOnWindows('abc', processManager: processManager); - expect(result, equals(false)); - }); - - test('when all processes are killed', () async { - pid = '123'; - output = '$pid abc'; - when(processManager.runSync(['powershell', 'Get-CimInstance', 'Win32_Process'])) - .thenAnswer((_) => ProcessResult(1, 0, output, 'def')); - when(processManager.runSync(['taskkill', '/pid', pid, '/f'])).thenAnswer((_) { - pid = ''; - output = ''; - return ProcessResult(1, 0, 'test', 'test'); - }); - final bool result = await killAllRunningProcessesOnWindows('abc', processManager: processManager); - expect(result, equals(true)); - }); - }); -} diff --git a/cipd_packages/device_doctor/test/src/ios_debug_symbol_doctor_test.dart b/cipd_packages/device_doctor/test/src/ios_debug_symbol_doctor_test.dart deleted file mode 100644 index 0d1a95e96..000000000 --- a/cipd_packages/device_doctor/test/src/ios_debug_symbol_doctor_test.dart +++ /dev/null @@ -1,325 +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:io'; - -import 'package:args/command_runner.dart'; -import 'package:device_doctor/src/ios_debug_symbol_doctor.dart'; -import 'package:fake_async/fake_async.dart'; -import 'package:file/memory.dart'; -import 'package:logging/logging.dart'; -import 'package:mockito/mockito.dart'; -import 'package:platform/platform.dart'; - -import 'package:test/test.dart'; - -import 'utils.dart'; - -Future main() async { - for (final String deviceName in const ['iPhone', 'iPhone 11', "Flutter's iOS Phone"]) { - test('ios_debug_symbol_doctor surfaces "Fetching debug symbols" error messages for "$deviceName"', () { - final Iterable devices = XCDevice.parseJson(_jsonWithErrors(deviceName)); - final Iterable erroredDevices = devices.where((XCDevice device) { - return device.hasError; - }); - expect(erroredDevices, hasLength(1)); - final XCDevice erroredDevice = erroredDevices.single; - expect(erroredDevice.error!['code'], -10); - expect(erroredDevice.error!['failureReason'], isEmpty); - expect(erroredDevice.error!['description'], '$deviceName is busy: Fetching debug symbols for $deviceName'); - expect(erroredDevice.error!['recoverySuggestion'], 'Xcode will continue when $deviceName is finished.'); - expect(erroredDevice.error!['domain'], 'com.apple.platform.iphoneos'); - }); - - test('ios_debug_symbol_doctor surfaces "Preparing $deviceName for development" error message', () { - final Iterable devices = XCDevice.parseJson(_jsonWithPreparingErrors(deviceName)); - final Iterable erroredDevices = devices.where((XCDevice device) { - return device.hasError; - }); - expect(erroredDevices, hasLength(1), reason: devices.toString()); - final XCDevice erroredDevice = erroredDevices.single; - expect(erroredDevice.error!['code'], -10); - expect(erroredDevice.error!['failureReason'], isEmpty); - expect( - erroredDevice.error!['description'], - contains('$deviceName is busy: Preparing $deviceName for development'), - ); - expect(erroredDevice.error!['recoverySuggestion'], 'Xcode will continue when $deviceName is finished.'); - expect(erroredDevice.error!['domain'], 'com.apple.platform.iphoneos'); - }); - } - - test('XCDevice ignores "phone is locked" errors', () { - final Iterable devices = XCDevice.parseJson(_jsonWithNonFatalErrors); - final Iterable erroredDevices = devices.where((XCDevice device) { - return device.hasError; - }); - expect(erroredDevices, isEmpty); - }); - - CommandRunner _createTestRunner() { - return CommandRunner( - 'ios-debug-symbol-doctor', - 'for testing', - ); - } - - group('commands', () { - late MockProcessManager processManager; - late TestLogger logger; - const String cocoonPath = '/path/to/cocoon'; - const String xcworkspacePath = '$cocoonPath/dashboard/ios/Runner.xcodeproj/project.xcworkspace'; - late MemoryFileSystem fs; - late Platform platform; - const String deviceSupportDirectory = '/User/username/Library/Developer/Xcode/iOS DeviceSupport'; - - setUp(() { - processManager = MockProcessManager(); - logger = TestLogger(); - fs = MemoryFileSystem(); - fs.directory(xcworkspacePath).createSync(recursive: true); - platform = MockPlatform(); - platform.environment['HOME'] = '/User/username'; - }); - - test('diagnose logs output of xcdevice list', () async { - when( - processManager.run(['xcrun', 'xcdevice', 'list']), - ).thenAnswer((_) async { - return ProcessResult(0, 0, _jsonWithNonFatalErrors, ''); - }); - final CommandRunner runner = _createTestRunner(); - final command = DiagnoseCommand( - processManager: processManager, - loggerOverride: logger, - ); - runner.addCommand(command); - await runner.run(['diagnose']); - expect(logger.logs[Level.INFO], contains(_jsonWithNonFatalErrors)); - }); - - test('recover returns early if xcodebuild -runFirstLaunch exits non-zero', () async { - final Directory symbolsDirectory = fs.directory('/User/username/Library/Developer/Xcode/iOS Temp'); - symbolsDirectory.createSync(recursive: true); - when( - processManager.run(['xcrun', 'xcodebuild', '-runFirstLaunch']), - ).thenAnswer((_) async { - return ProcessResult( - 0, - 1, - '', - 'xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun', - ); - }); - - fakeAsync((FakeAsync time) { - final CommandRunner runner = _createTestRunner(); - final command = RecoverCommand( - processManager: processManager, - loggerOverride: logger, - fs: fs, - platform: platform, - ); - runner.addCommand(command); - bool? result; - runner.run(['recover', '--cocoon-root=$cocoonPath']).then((bool? value) => result = value); - time.elapse(const Duration(microseconds: 1)); - expect(result, isNotNull); - }); - }); - - test('recover deletes symbols, opens Xcode, waits, then kills it', () async { - final Directory symbolsDirectory = fs.directory(deviceSupportDirectory); - symbolsDirectory.createSync(recursive: true); - when( - processManager.run(['xcrun', 'xcodebuild', '-runFirstLaunch']), - ).thenAnswer((_) async { - return ProcessResult(0, 0, '', ''); - }); - when( - processManager.run(['open', '-n', '-F', '-W', xcworkspacePath]), - ).thenAnswer((_) async { - return ProcessResult(1, 0, '', ''); - }); - bool killedXcode = false; - when( - processManager.run(['killall', '-9', 'Xcode']), - ).thenAnswer((_) async { - killedXcode = true; - return ProcessResult(2, 0, '', ''); - }); - final bool result = fakeAsync((FakeAsync time) { - final CommandRunner runner = _createTestRunner(); - final command = RecoverCommand( - processManager: processManager, - loggerOverride: logger, - fs: fs, - platform: platform, - ); - runner.addCommand(command); - bool? result; - runner.run(['recover', '--cocoon-root=$cocoonPath']).then((bool? value) => result = value); - time.elapse(const Duration(seconds: 299)); - // We have not yet reached the timeout, so Xcode should still be open - expect(result, isNull); - expect(killedXcode, isFalse); - time.elapse(const Duration(seconds: 2)); - expect(result, isNotNull); - expect(killedXcode, isTrue); - expect(logger.logs[Level.WARNING], isNull); - expect(symbolsDirectory.existsSync(), false); - - return result!; - }); - expect(result, isTrue); - expect( - logger.logs[Level.INFO], - containsAllInOrder([ - 'Running Xcode first launch...', - 'Launching Xcode...', - 'Waiting for 300 seconds', - 'Waited for 300 seconds, now killing Xcode', - ]), - ); - }); - - test('recover cannot find symbols but still opens Xcode, waits, then kills it', () async { - when( - processManager.run(['xcrun', 'xcodebuild', '-runFirstLaunch']), - ).thenAnswer((_) async { - return ProcessResult(0, 0, '', ''); - }); - when( - processManager.run(['open', '-n', '-F', '-W', xcworkspacePath]), - ).thenAnswer((_) async { - return ProcessResult(1, 0, '', ''); - }); - bool killedXcode = false; - when( - processManager.run(['killall', '-9', 'Xcode']), - ).thenAnswer((_) async { - killedXcode = true; - return ProcessResult(2, 0, '', ''); - }); - final bool result = fakeAsync((FakeAsync time) { - final CommandRunner runner = _createTestRunner(); - final command = RecoverCommand( - processManager: processManager, - loggerOverride: logger, - fs: fs, - platform: platform, - ); - runner.addCommand(command); - bool? result; - runner.run(['recover', '--cocoon-root=$cocoonPath']).then((bool? value) => result = value); - time.elapse(const Duration(seconds: 299)); - // We have not yet reached the timeout, so Xcode should still be open - expect(result, isNull); - expect(killedXcode, isFalse); - time.elapse(const Duration(seconds: 2)); - expect(result, isNotNull); - expect(killedXcode, isTrue); - expect( - logger.logs[Level.WARNING], - contains('iOS Device Support directory was not found at $deviceSupportDirectory'), - ); - return result!; - }); - expect(result, isTrue); - expect( - logger.logs[Level.INFO], - containsAllInOrder([ - 'Running Xcode first launch...', - 'Launching Xcode...', - 'Waiting for 300 seconds', - 'Waited for 300 seconds, now killing Xcode', - ]), - ); - }); - }); -} - -const String _jsonWithNonFatalErrors = ''' -[ - { - "modelCode" : "iPhone11,8", - "simulator" : false, - "modelName" : "iPhone XR", - "error" : { - "code" : -13, - "failureReason" : "", - "underlyingErrors" : [ - { - "code" : 4, - "failureReason" : "", - "description" : "Flutter’s iPhone is locked.", - "recoverySuggestion" : "To use Flutter’s iPhone with Xcode, unlock it.", - "domain" : "DVTDeviceIneligibilityErrorDomain" - } - ], - "description" : "Flutter’s iPhone is not connected", - "recoverySuggestion" : "Xcode will continue when Flutter’s iPhone is connected.", - "domain" : "com.apple.platform.iphoneos" - }, - "operatingSystemVersion" : "15.4.1 (19E258)", - "identifier" : "00008120-00017DA80CC1002E", - "platform" : "com.apple.platform.iphoneos", - "architecture" : "arm64e", - "interface" : "usb", - "available" : false, - "name" : "Flutter’s iPhone", - "modelUTI" : "com.apple.iphone-xr-9" - } -]'''; - -String _jsonWithErrors(String name) => ''' -[ - { - "modelCode" : "iPhone8,1", - "simulator" : false, - "modelName" : "iPhone 6s", - "error" : { - "code" : -10, - "failureReason" : "", - "description" : "$name is busy: Fetching debug symbols for $name", - "recoverySuggestion" : "Xcode will continue when $name is finished.", - "domain" : "com.apple.platform.iphoneos" - }, - "operatingSystemVersion" : "15.1 (19B74)", - "identifier" : "e3f3a0cf8005b8b34f14d16fa224b19017648353", - "platform" : "com.apple.platform.iphoneos", - "architecture" : "arm64", - "interface" : "usb", - "available" : false, - "name" : "$name", - "modelUTI" : "com.apple.iphone-6s-e1ccb7" - } -] -'''; - -String _jsonWithPreparingErrors(String name) => ''' -[ - { - "modelCode" : "iPhone8,1", - "simulator" : false, - "modelName" : "iPhone 6s", - "error" : { - "code" : -10, - "failureReason" : "", - "description" : "$name is busy: Preparing $name for development. Xcode will continue when $name is finished. (code -10)", - "recoverySuggestion" : "Xcode will continue when $name is finished.", - "domain" : "com.apple.platform.iphoneos" - }, - "operatingSystemVersion" : "15.1 (19B74)", - "identifier" : "e3f3a0cf8005b8b34f14d16fa224b19017648353", - "platform" : "com.apple.platform.iphoneos", - "architecture" : "arm64", - "interface" : "usb", - "available" : false, - "name" : "$name", - "modelUTI" : "com.apple.iphone-6s-e1ccb7" - } -] -'''; diff --git a/cipd_packages/device_doctor/test/src/ios_device_test.dart b/cipd_packages/device_doctor/test/src/ios_device_test.dart deleted file mode 100644 index 675433c2f..000000000 --- a/cipd_packages/device_doctor/test/src/ios_device_test.dart +++ /dev/null @@ -1,505 +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:io'; - -import 'package:file/src/backends/memory/memory_file_system.dart'; -import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; - -import 'package:device_doctor/src/device.dart'; -import 'package:device_doctor/src/health.dart'; -import 'package:device_doctor/src/ios_device.dart'; -import 'package:device_doctor/src/utils.dart'; - -import 'fake_ios_device.dart'; -import 'utils.dart'; - -void main() { - group('IosDeviceDiscovery', () { - late FakeIosDeviceDiscovery deviceDiscovery; - late MockProcessManager processManager; - Process process; - - setUp(() { - deviceDiscovery = FakeIosDeviceDiscovery(MemoryFileSystem().file('output')); - processManager = MockProcessManager(); - }); - - test('deviceDiscovery', () async { - deviceDiscovery.outputs = ['']; - expect(await deviceDiscovery.discoverDevices(), isEmpty); - final StringBuffer sb = StringBuffer(); - sb.writeln('abcdefg'); - deviceDiscovery.outputs = [sb.toString()]; - final List devices = await deviceDiscovery.discoverDevices(); - expect(devices.length, equals(1)); - expect(devices[0].deviceId, equals('abcdefg')); - }); - - test('checkDevices without device', () async { - deviceDiscovery.outputs = ['']; - final Map> results = await deviceDiscovery.checkDevices(); - await expectLater(results.keys.length, 0); - }); - - test('checkDevices with device', () async { - process = FakeProcess(0, out: >[]); - when(processManager.start(any, workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(process)); - deviceDiscovery.outputs = ['abcdefg']; - final Map> results = - await deviceDiscovery.checkDevices(processManager: processManager); - expect(results['ios-device-abcdefg'], isNotNull); - expect(results.keys.length, equals(1)); - expect(results.keys.toList()[0], 'ios-device-abcdefg'); - final List healthCheckResults = results['ios-device-abcdefg']!; - expect(healthCheckResults.length, 7); - expect(healthCheckResults[0].name, kDeviceAccessCheckKey); - expect(healthCheckResults[0].succeeded, true); - expect(healthCheckResults[1].name, kKeychainUnlockCheckKey); - expect(healthCheckResults[1].succeeded, true); - expect(healthCheckResults[2].name, kCertCheckKey); - expect(healthCheckResults[2].succeeded, false); - expect(healthCheckResults[3].name, kDevicePairCheckKey); - expect(healthCheckResults[3].succeeded, false); - expect(healthCheckResults[4].name, kUserAutoLoginCheckKey); - expect(healthCheckResults[4].succeeded, false); - expect(healthCheckResults[5].name, kDeviceProvisioningProfileCheckKey); - expect(healthCheckResults[5].succeeded, false); - expect(healthCheckResults[6].name, kBatteryLevelCheckKey); - expect(healthCheckResults[6].succeeded, false); - }); - }); - - group('IosDeviceDiscovery - health checks', () { - late IosDeviceDiscovery deviceDiscovery; - late MockProcessManager processManager; - Process process; - List> output; - - setUp(() { - processManager = MockProcessManager(); - deviceDiscovery = IosDeviceDiscovery(MemoryFileSystem().file('output')); - }); - - test('Keychain unlock check - success', () async { - process = FakeProcess(0); - when(processManager.start(any, workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(process)); - final HealthCheckResult healthCheckResult = - await deviceDiscovery.keychainUnlockCheck(processManager: processManager); - expect(healthCheckResult.succeeded, true); - }); - - test('Keychain unlock check - exception', () async { - process = FakeProcess(1); - when(processManager.start(any, workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(process)); - final HealthCheckResult healthCheckResult = - await deviceDiscovery.keychainUnlockCheck(processManager: processManager); - expect(healthCheckResult.succeeded, false); - expect(healthCheckResult.name, kKeychainUnlockCheckKey); - expect(healthCheckResult.details, 'Executable ${kUnlockLoginKeychain} failed with exit code 1.'); - }); - - test('Cert check - success', () async { - final StringBuffer sb = StringBuffer(); - sb.writeln('1) abcdefg "Apple Development: Flutter Devicelab (hijklmn)"'); - sb.writeln('1 valid identities found'); - output = >[utf8.encode(sb.toString())]; - process = FakeProcess(0, out: output); - when(processManager.start(any, workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(process)); - final HealthCheckResult healthCheckResult = await deviceDiscovery.certCheck(processManager: processManager); - expect(healthCheckResult.succeeded, true); - }); - - test('Cert check - failure without target certificate', () async { - final StringBuffer sb = StringBuffer(); - sb.writeln('abcdefg'); - sb.writeln('hijklmn'); - output = >[utf8.encode(sb.toString())]; - process = FakeProcess(0, out: output); - when(processManager.start(any, workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(process)); - final HealthCheckResult healthCheckResult = await deviceDiscovery.certCheck(processManager: processManager); - expect(healthCheckResult.succeeded, false); - expect(healthCheckResult.name, kCertCheckKey); - expect(healthCheckResult.details, sb.toString().trim()); - }); - - test('Cert check - failure with multiple certificates', () async { - final StringBuffer sb = StringBuffer(); - sb.writeln('1) abcdefg "Apple Development: Flutter Devicelab (hijklmn)"'); - - sb.writeln('1) opqrst "uvwxyz"'); - sb.writeln('2 valid identities found'); - output = >[utf8.encode(sb.toString())]; - process = FakeProcess(0, out: output); - when(processManager.start(any, workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(process)); - final HealthCheckResult healthCheckResult = await deviceDiscovery.certCheck(processManager: processManager); - expect(healthCheckResult.succeeded, false); - expect(healthCheckResult.name, kCertCheckKey); - expect(healthCheckResult.details, sb.toString().trim()); - }); - - test('Cert check - failure with revoked certificates', () async { - final StringBuffer sb = StringBuffer(); - sb.writeln('1) abcdefg "Apple Development: Flutter Devicelab (hijklmn)" (CSSMERR_TP_CERT_REVOKED)'); - sb.writeln('1 valid identities found'); - output = >[utf8.encode(sb.toString())]; - process = FakeProcess(0, out: output); - when(processManager.start(any, workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(process)); - final HealthCheckResult healthCheckResult = await deviceDiscovery.certCheck(processManager: processManager); - expect(healthCheckResult.succeeded, false); - expect(healthCheckResult.name, kCertCheckKey); - expect(healthCheckResult.details, sb.toString().trim()); - }); - - test('Cert check - exception', () async { - process = FakeProcess(1); - when(processManager.start(any, workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(process)); - final HealthCheckResult healthCheckResult = await deviceDiscovery.certCheck(processManager: processManager); - expect(healthCheckResult.succeeded, false); - expect(healthCheckResult.name, kCertCheckKey); - expect(healthCheckResult.details, 'Executable security failed with exit code 1.'); - }); - - test('Device pair check - success', () async { - final StringBuffer sb = StringBuffer(); - sb.writeln('SUCCESS: Validated pairing with device abcdefg-hijklmn'); - output = >[utf8.encode(sb.toString())]; - process = FakeProcess(0, out: output); - when(processManager.start(any, workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(process)); - final HealthCheckResult healthCheckResult = await deviceDiscovery.devicePairCheck(processManager: processManager); - expect(healthCheckResult.succeeded, true); - }); - - test('Device pair check - failure', () async { - final StringBuffer sb = StringBuffer(); - sb.writeln('abcdefg'); - output = >[utf8.encode(sb.toString())]; - process = FakeProcess(0, out: output); - when(processManager.start(any, workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(process)); - final HealthCheckResult healthCheckResult = await deviceDiscovery.devicePairCheck(processManager: processManager); - expect(healthCheckResult.succeeded, false); - expect(healthCheckResult.name, kDevicePairCheckKey); - expect(healthCheckResult.details, sb.toString().trim()); - }); - - test('Device pair check - exception', () async { - process = FakeProcess(1); - when(processManager.start(any, workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(process)); - final HealthCheckResult healthCheckResult = await deviceDiscovery.devicePairCheck(processManager: processManager); - expect(healthCheckResult.succeeded, false); - expect(healthCheckResult.name, kDevicePairCheckKey); - expect(healthCheckResult.details, 'Executable idevicepair failed with exit code 1.'); - }); - - group('Device provisioning profile check', () { - Process lsProcess; - Process securityProcess; - List> lsOutput; - List> securityOutput; - test('success', () async { - const String fileName = 'abcdefg'; - lsOutput = >[utf8.encode(fileName)]; - lsProcess = FakeProcess(0, out: lsOutput); - - const String deviceID = 'deviceId'; - const String profileContent = ''' - test1 - $deviceID - test2 - - '''; - securityOutput = >[utf8.encode(profileContent)]; - securityProcess = FakeProcess(0, out: securityOutput); - - final String? homeDir = Platform.environment['HOME']; - when( - processManager.start( - ['ls', '$homeDir/Library/MobileDevice/Provisioning\ Profiles'], - workingDirectory: anyNamed('workingDirectory'), - ), - ).thenAnswer((_) => Future.value(lsProcess)); - when( - processManager.start( - ['security', 'cms', '-D', '-i', '$homeDir/Library/MobileDevice/Provisioning\ Profiles/$fileName'], - workingDirectory: anyNamed('workingDirectory'), - ), - ).thenAnswer((_) => Future.value(securityProcess)); - - final HealthCheckResult healthCheckResult = - await deviceDiscovery.deviceProvisioningProfileCheck(deviceID, processManager: processManager); - expect(healthCheckResult.succeeded, true); - expect(healthCheckResult.name, kDeviceProvisioningProfileCheckKey); - }); - - test('deviceId does not exist', () async { - const String fileName = 'abcdefg'; - lsOutput = >[utf8.encode(fileName)]; - lsProcess = FakeProcess(0, out: lsOutput); - - const String deviceID = 'deviceId'; - const String profileContent = ''' - test1 - test2 - - '''; - securityOutput = >[utf8.encode(profileContent)]; - securityProcess = FakeProcess(0, out: securityOutput); - - final String? homeDir = Platform.environment['HOME']; - when( - processManager.start( - ['ls', '$homeDir/Library/MobileDevice/Provisioning\ Profiles'], - workingDirectory: anyNamed('workingDirectory'), - ), - ).thenAnswer((_) => Future.value(lsProcess)); - when( - processManager.start( - ['security', 'cms', '-D', '-i', '$homeDir/Library/MobileDevice/Provisioning\ Profiles/$fileName'], - workingDirectory: anyNamed('workingDirectory'), - ), - ).thenAnswer((_) => Future.value(securityProcess)); - - final HealthCheckResult healthCheckResult = - await deviceDiscovery.deviceProvisioningProfileCheck(deviceID, processManager: processManager); - expect(healthCheckResult.succeeded, false); - expect(healthCheckResult.name, kDeviceProvisioningProfileCheckKey); - expect(healthCheckResult.details, 'device does not exist in the provisioning profile'); - }); - }); - - group('Device battery check', () { - Process process; - List> output; - test('battery level is okay', () async { - const String batteryLevel = '100'; - output = >[utf8.encode(batteryLevel)]; - process = FakeProcess(0, out: output); - - when( - processManager.start( - ['ideviceinfo', '-q', 'com.apple.mobile.battery', '-k', 'BatteryCurrentCapacity'], - workingDirectory: anyNamed('workingDirectory'), - ), - ).thenAnswer((_) => Future.value(process)); - - final HealthCheckResult healthCheckResult = - await deviceDiscovery.batteryLevelCheck(processManager: processManager); - expect(healthCheckResult.succeeded, true); - expect(healthCheckResult.name, kBatteryLevelCheckKey); - }); - - test('battery level is below minLevel', () async { - const String batteryLevel = '10'; - output = >[utf8.encode(batteryLevel)]; - process = FakeProcess(0, out: output); - - when( - processManager.start( - ['ideviceinfo', '-q', 'com.apple.mobile.battery', '-k', 'BatteryCurrentCapacity'], - workingDirectory: anyNamed('workingDirectory'), - ), - ).thenAnswer((_) => Future.value(process)); - - final HealthCheckResult healthCheckResult = - await deviceDiscovery.batteryLevelCheck(processManager: processManager); - expect(healthCheckResult.succeeded, false); - expect(healthCheckResult.name, kBatteryLevelCheckKey); - expect(healthCheckResult.details, 'Battery level ($batteryLevel) is below 15'); - }); - }); - }); - - group('IosDevice recovery checks', () { - IosDevice device; - late MockProcessManager processManager; - Process process; - Process whichProcess; - String output; - String ideviceinstallerPath; - String idevicediagnosticsPath; - - setUp(() { - processManager = MockProcessManager(); - }); - test('device restart - success', () async { - idevicediagnosticsPath = '/abc/def/idevicediagnostics'; - whichProcess = FakeProcess(0, out: >[utf8.encode(idevicediagnosticsPath)]); - when( - processManager.start(['which', 'idevicediagnostics'], workingDirectory: anyNamed('workingDirectory')), - ).thenAnswer((_) => Future.value(whichProcess)); - process = FakeProcess(0); - device = IosDevice(deviceId: 'abc'); - when( - processManager - .start([idevicediagnosticsPath, 'restart'], workingDirectory: anyNamed('workingDirectory')), - ).thenAnswer((_) => Future.value(process)); - final bool result = await device.restart_device(processManager: processManager); - expect(result, isTrue); - }); - - test('device restart - failure', () async { - idevicediagnosticsPath = '/abc/def/idevicediagnostics'; - whichProcess = FakeProcess(0, out: >[utf8.encode(idevicediagnosticsPath)]); - when( - processManager.start(['which', 'idevicediagnostics'], workingDirectory: anyNamed('workingDirectory')), - ).thenAnswer((_) => Future.value(whichProcess)); - process = FakeProcess(1); - device = IosDevice(deviceId: 'abc'); - when( - processManager - .start([idevicediagnosticsPath, 'restart'], workingDirectory: anyNamed('workingDirectory')), - ).thenAnswer((_) => Future.value(process)); - final bool result = await device.restart_device(processManager: processManager); - expect(result, isFalse); - }); - - test('device restart - skip 32 bit phone', () async { - device = IosDevice(deviceId: '822ef7958bba573829d85eef4df6cbdd86593730'); - final bool result = await device.restart_device(processManager: processManager); - expect(result, isTrue); - }); - - test('list applications - failure', () async { - device = IosDevice(deviceId: '822ef7958bba573829d85eef4df6cbdd86593730'); - process = FakeProcess(1); - ideviceinstallerPath = '/abc/def/ideviceinstaller'; - whichProcess = FakeProcess(0, out: >[utf8.encode(ideviceinstallerPath)]); - when(processManager.start(['which', 'ideviceinstaller'], workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(whichProcess)); - when(processManager.start([ideviceinstallerPath, '-l'], workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(process)); - - final bool result = await device.uninstall_applications(processManager: processManager); - expect(result, isFalse); - }); - - test('uninstall applications - no device is available', () async { - ideviceinstallerPath = '/abc/def/ideviceinstaller'; - device = IosDevice(deviceId: '822ef7958bba573829d85eef4df6cbdd86593730'); - output = '''No device found. - '''; - process = FakeProcess(0, out: >[utf8.encode(output)]); - whichProcess = FakeProcess(0, out: >[utf8.encode(ideviceinstallerPath)]); - when(processManager.start(['which', 'ideviceinstaller'], workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(whichProcess)); - when(processManager.start([ideviceinstallerPath, '-l'], workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(process)); - final bool result = await device.uninstall_applications(processManager: processManager); - expect(result, isTrue); - }); - - test('uninstall applications - no application exist', () async { - ideviceinstallerPath = '/abc/def/ideviceinstaller'; - device = IosDevice(deviceId: '822ef7958bba573829d85eef4df6cbdd86593730'); - output = '''CFBundleIdentifier, CFBundleVersion, CFBundleDisplayName - '''; - process = FakeProcess(0, out: >[utf8.encode(output)]); - whichProcess = FakeProcess(0, out: >[utf8.encode(ideviceinstallerPath)]); - when(processManager.start(['which', 'ideviceinstaller'], workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(whichProcess)); - when(processManager.start([ideviceinstallerPath, '-l'], workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(process)); - - final bool result = await device.uninstall_applications(processManager: processManager); - expect(result, isTrue); - }); - - test('uninstall applications - applications exist with exception', () async { - ideviceinstallerPath = '/abc/def/ideviceinstaller'; - device = IosDevice(deviceId: '822ef7958bba573829d85eef4df6cbdd86593730'); - whichProcess = FakeProcess(0, out: >[utf8.encode(ideviceinstallerPath)]); - output = '''CFBundleIdentifier, CFBundleVersion, CFBundleDisplayName - abc, def, ghi - jkl, mno, pqr - '''; - process = FakeProcess(0, out: >[utf8.encode(output)]); - Process process_uninstall = FakeProcess(1); - when(processManager.start(['which', 'ideviceinstaller'], workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(whichProcess)); - when(processManager.start([ideviceinstallerPath, '-l'], workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(process)); - when( - processManager - .start([ideviceinstallerPath, '-U', 'abc'], workingDirectory: anyNamed('workingDirectory')), - ).thenAnswer((_) => Future.value(process_uninstall)); - when( - processManager - .start([ideviceinstallerPath, '-U', 'jkl'], workingDirectory: anyNamed('workingDirectory')), - ).thenAnswer((_) => Future.value(process_uninstall)); - - output = '''CFBundleIdentifier, CFBundleVersion, CFBundleDisplayName - abc, def, ghi - jkl, mno, pqr - '''; - process = FakeProcess(0, out: >[utf8.encode(output)]); - process_uninstall = FakeProcess(1); - - final bool result = await device.uninstall_applications(processManager: processManager); - expect(result, isFalse); - }); - - test('uninstall applications - applications exist', () async { - ideviceinstallerPath = '/abc/def/ideviceinstaller'; - device = IosDevice(deviceId: '822ef7958bba573829d85eef4df6cbdd86593730'); - whichProcess = FakeProcess(0, out: >[utf8.encode(ideviceinstallerPath)]); - output = '''CFBundleIdentifier, CFBundleVersion, CFBundleDisplayName - abc, def, ghi - jkl, mno, pqr - '''; - process = FakeProcess(0, out: >[utf8.encode(output)]); - final Process process_uninstall = FakeProcess(0); - when(processManager.start(['which', 'ideviceinstaller'], workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(whichProcess)); - when(processManager.start([ideviceinstallerPath, '-l'], workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(process)); - when( - processManager - .start([ideviceinstallerPath, '-U', 'abc'], workingDirectory: anyNamed('workingDirectory')), - ).thenAnswer((_) => Future.value(process_uninstall)); - when( - processManager - .start([ideviceinstallerPath, '-U', 'jkl'], workingDirectory: anyNamed('workingDirectory')), - ).thenAnswer((_) => Future.value(process_uninstall)); - - final bool result = await device.uninstall_applications(processManager: processManager); - expect(result, isTrue); - }); - }); - - group('deviceListOutput', () { - late IosDeviceDiscovery deviceDiscovery; - late MockProcessManager processManager; - Process processIdeviceID; - Process processWhichIdeviceID; - - setUp(() { - processManager = MockProcessManager(); - deviceDiscovery = IosDeviceDiscovery(MemoryFileSystem().file('output')); - }); - - test('success', () async { - processIdeviceID = FakeProcess(0, out: >[utf8.encode('abc')]); - processWhichIdeviceID = FakeProcess(0, out: >[utf8.encode('/test/idevice_id')]); - when(processManager.start(['which', 'idevice_id'], workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(processWhichIdeviceID)); - when(processManager.start(['/test/idevice_id', '-l'], workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(processIdeviceID)); - final String deviceId = await deviceDiscovery.deviceListOutput(processManager: processManager); - expect(deviceId, 'abc'); - }); - }); -} diff --git a/cipd_packages/device_doctor/test/src/mac_test.dart b/cipd_packages/device_doctor/test/src/mac_test.dart deleted file mode 100644 index f004038e7..000000000 --- a/cipd_packages/device_doctor/test/src/mac_test.dart +++ /dev/null @@ -1,57 +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:io'; - -import 'package:mockito/mockito.dart'; -import 'package:test/test.dart'; - -import 'package:device_doctor/src/health.dart'; -import 'package:device_doctor/src/mac.dart'; -import 'package:device_doctor/src/utils.dart'; - -import 'utils.dart'; - -void main() { - group('Mac - health checks', () { - late MockProcessManager processManager; - late Process process; - List> output; - - setUp(() { - processManager = MockProcessManager(); - }); - - test('Swarming user auto login check - success', () async { - when(processManager.start(any, workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(process)); - output = >[utf8.encode('swarming')]; - process = FakeProcess(0, out: output); - final HealthCheckResult healthCheckResult = await userAutoLoginCheck(processManager: processManager); - expect(healthCheckResult.succeeded, true); - }); - - test('Swarming user auto login check - exception', () async { - when(processManager.start(any, workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(process)); - process = FakeProcess(1); - final HealthCheckResult healthCheckResult = await userAutoLoginCheck(processManager: processManager); - expect(healthCheckResult.succeeded, false); - expect(healthCheckResult.name, kUserAutoLoginCheckKey); - expect(healthCheckResult.details, 'Executable defaults failed with exit code 1.'); - }); - - test('Swarming user auto login check - failure', () async { - when(processManager.start(any, workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(process)); - output = >[utf8.encode('test')]; - process = FakeProcess(0, out: output); - final HealthCheckResult healthCheckResult = await userAutoLoginCheck(processManager: processManager); - expect(healthCheckResult.succeeded, false); - expect(healthCheckResult.name, kUserAutoLoginCheckKey); - expect(healthCheckResult.details, 'swarming user is not setup for auto login'); - }); - }); -} diff --git a/cipd_packages/device_doctor/test/src/utils.dart b/cipd_packages/device_doctor/test/src/utils.dart deleted file mode 100644 index 2792a071c..000000000 --- a/cipd_packages/device_doctor/test/src/utils.dart +++ /dev/null @@ -1,128 +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:logging/logging.dart'; -import 'package:mockito/mockito.dart'; -import 'package:process/process.dart'; -import 'package:platform/platform.dart'; - -class MockPlatform extends Mock implements Platform { - @override - Map environment = {}; -} - -class MockProcessManager extends Mock implements ProcessManager { - @override - Future start( - List? command, { - String? workingDirectory, - Map? environment, - bool includeParentEnvironment = true, - bool runInShell = false, - ProcessStartMode mode = ProcessStartMode.normal, - }) { - return super.noSuchMethod( - Invocation.method(#start, [command], {#workingDirectory: workingDirectory}), - returnValue: Future.value(FakeProcess(0)), - ); - } - - @override - ProcessResult runSync( - List command, { - String? workingDirectory, - Map? environment, - bool includeParentEnvironment = true, - bool runInShell = false, - covariant Encoding? stdoutEncoding = systemEncoding, - covariant Encoding? stderrEncoding = systemEncoding, - }) { - return super.noSuchMethod(Invocation.method(#runSync, [command]), returnValue: ProcessResult(1, 0, 'abc', 'def')); - } - - @override - Future run( - List command, { - String? workingDirectory, - Map? environment, - bool includeParentEnvironment = true, - bool runInShell = false, - covariant Encoding? stdoutEncoding = systemEncoding, - covariant Encoding? stderrEncoding = systemEncoding, - }) { - return super - .noSuchMethod(Invocation.method(#run, [command]), returnValue: Future.value(ProcessResult(1, 0, 'abc', 'def'))); - } -} - -class FakeProcess extends Fake implements Process { - FakeProcess( - int exitCode, { - List>? err = const [ - [1, 2, 3], - ], - List>? out = const [ - [1, 2, 3], - ], - }) : _exitCode = exitCode, - _err = err, - _out = out; - - final int _exitCode; - final List>? _err; - final List>? _out; - - @override - Future get exitCode => Future.value(_exitCode); - - @override - Stream> get stderr => Stream.fromIterable(_err ?? >[]); - - @override - Stream> get stdout => Stream.fromIterable(_out ?? >[utf8.encode('test')]); -} - -class TestLogger implements Logger { - final String name = 'test-logger'; - String get fullName => name; - void clearListeners() => throw UnimplementedError('Unimplemented!'); - bool isLoggable(Level value) => throw UnimplementedError('Unimplemented!'); - Level get level => throw UnimplementedError('Unimplemented!'); - set level(Level? value) => throw UnimplementedError('Unimplemented!'); - Stream get onRecord => throw UnimplementedError('Unimplemented!'); - Stream get onLevelChanged => throw UnimplementedError('Unimplemented!'); - final Logger? parent = null; - final Map children = const {}; - - final Map> logs = >{}; - - void log(Level logLevel, Object? message, [Object? error, StackTrace? stackTrace, Zone? zone]) { - logs[logLevel] ??= []; - logs[logLevel]!.add(message.toString()); - } - - void finest(Object? message, [Object? error, StackTrace? stackTrace]) => - log(Level.FINEST, message, error, stackTrace); - - void finer(Object? message, [Object? error, StackTrace? stackTrace]) => log(Level.FINER, message, error, stackTrace); - - void fine(Object? message, [Object? error, StackTrace? stackTrace]) => log(Level.FINE, message, error, stackTrace); - - void config(Object? message, [Object? error, StackTrace? stackTrace]) => - log(Level.CONFIG, message, error, stackTrace); - - void info(Object? message, [Object? error, StackTrace? stackTrace]) => log(Level.INFO, message, error, stackTrace); - - void warning(Object? message, [Object? error, StackTrace? stackTrace]) => - log(Level.WARNING, message, error, stackTrace); - - void severe(Object? message, [Object? error, StackTrace? stackTrace]) => - log(Level.SEVERE, message, error, stackTrace); - - void shout(Object? message, [Object? error, StackTrace? stackTrace]) => log(Level.SHOUT, message, error, stackTrace); -} diff --git a/cipd_packages/device_doctor/test/src/utils_test.dart b/cipd_packages/device_doctor/test/src/utils_test.dart deleted file mode 100644 index 0e9016864..000000000 --- a/cipd_packages/device_doctor/test/src/utils_test.dart +++ /dev/null @@ -1,121 +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:mockito/mockito.dart'; -import 'package:test/test.dart'; - -import 'package:device_doctor/src/utils.dart'; - -import 'utils.dart'; - -void main() { - group('grep', () { - String pattern; - String from; - - test('with multiple matched lines', () async { - pattern = 'abc'; - from = 'abc\n' - 'def\n' - 'abcd'; - expect(grep(pattern, from: from), equals(['abc', 'abcd'])); - }); - }); - - group('startProcess', () { - late MockProcessManager processManager; - List> output; - - setUp(() { - processManager = MockProcessManager(); - }); - - test('validate process', () async { - final StringBuffer sb = StringBuffer(); - sb.writeln('abc'); - output = >[utf8.encode(sb.toString())]; - final Process process = FakeProcess(123, out: output); - when(processManager.start(any, workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(process)); - - final Process proc = await startProcess('abc', ['a', 'b', 'c'], processManager: processManager); - expect(proc, process); - }); - }); - - group('eval', () { - late MockProcessManager processManager; - List> output; - Process process; - - setUp(() { - processManager = MockProcessManager(); - }); - - test('exit code 0', () async { - final StringBuffer sb = StringBuffer(); - sb.writeln('abc'); - output = >[utf8.encode(sb.toString())]; - process = FakeProcess(0, out: output); - when(processManager.start(any, workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(process)); - final String result = await eval('abc', ['a', 'b', 'c'], processManager: processManager); - expect('$result\n', sb.toString()); - }); - - test('exit code not 0', () async { - final StringBuffer sb = StringBuffer(); - sb.writeln('List of devices attached'); - sb.writeln('ZY223JQNMR device'); - output = >[utf8.encode(sb.toString())]; - process = FakeProcess(1, out: output); - expect( - eval('abc', ['a', 'b', 'c'], processManager: processManager), - throwsA(TypeMatcher()), - ); - }); - }); - - group('getMacBinaryPath', () { - late MockProcessManager processManager; - List> output; - - setUp(() { - processManager = MockProcessManager(); - }); - - test('returns path when binary exists by default', () async { - const String path = '/abc/def/ideviceinstaller'; - output = >[utf8.encode(path)]; - final Process process = FakeProcess(0, out: output); - when(processManager.start(['which', 'ideviceinstaller'], workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(process)); - - final String result = await getMacBinaryPath('ideviceinstaller', processManager: processManager); - expect(result, path); - }); - - test('throws exception when binary does not exist in any location', () async { - final Process processM1 = FakeProcess(1, out: >[]); - final Process processDefault = FakeProcess(1, out: >[]); - when(processManager.start(['which', 'ideviceinstaller'], workingDirectory: anyNamed('workingDirectory'))) - .thenAnswer((_) => Future.value(processDefault)); - when( - processManager.start( - ['which', 'ideviceinstaller'], - workingDirectory: anyNamed('workingDirectory'), - ), - ).thenAnswer((_) => Future.value(processM1)); - - expect( - getMacBinaryPath('ideviceinstaller', processManager: processManager), - throwsA(TypeMatcher()), - ); - }); - }); -} diff --git a/cipd_packages/device_doctor/tool/build.bat b/cipd_packages/device_doctor/tool/build.bat deleted file mode 100644 index e6e3e7db0..000000000 --- a/cipd_packages/device_doctor/tool/build.bat +++ /dev/null @@ -1,35 +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. - -REM Checks if cipd command is available. -FOR /F "tokens=*" %%g IN ('where cipd') do (SET CIPD=%%g) -IF %ERRORLEVEL% NEQ 0 ( - ECHO "Please install CIPD (available from depot_tools) and add to path first."; - EXIT -) - -REM `path` is \path\to\device_doctor\tool\ -REM `DIR` is \path\to\device_doctor\tool -SET path=%~dp0 -SET DIR=%path:~0,-1% -%CIPD% ensure --ensure-file %path%\ensure_file_windows -root %DIR% - -REM `BUILD_DIR` is \path\to\device_doctor -for %%a in (%DIR:~0,-1%) do set "BUILD_DIR=%%~dpa" -PUSHD %BUILD_DIR% -if exist %BUILD_DIR%\build ( - ECHO "Please remove the build directory before proceeding" - EXIT 1 -) -MKDIR %BUILD_DIR%\build - -call tool\bin\dart.exe pub get -call tool\bin\dart.exe compile exe bin\main.dart -o build\device_doctor.exe -IF "%ERRORLEVEL%" NEQ "0" ( - EXIT 1 -) - -REM Add PATH for xcopy -SET "PATH=%PATH%;C:\Windows\system32" -xcopy LICENSE %BUILD_DIR%\build\. diff --git a/cipd_packages/device_doctor/tool/build.sh b/cipd_packages/device_doctor/tool/build.sh deleted file mode 100755 index e32995481..000000000 --- a/cipd_packages/device_doctor/tool/build.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/usr/bin/env bash -# 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. - -# Fetches corresponding dart sdk from CIPD for different platforms, builds -# an executable binary of device_doctor to `build` folder. -# -# This currently supports linux and mac. - -set -e - -command -v cipd > /dev/null || { - echo "Please install CIPD (available from depot_tools) and add to path first."; - exit -1; -} - -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd)" -OS="`uname`" - -echo ########### Ensure file ########### -cat $DIR/ensure_file -echo ################################### - - -cipd ensure -ensure-file $DIR/ensure_file -root $DIR - -pushd $DIR/.. - -if [[ -d "build" ]]; then - echo "Please remove the build directory before proceeding" - exit -1 -fi - -mkdir -p build -pwd -ls tool -tool/bin/dart pub get -tool/bin/dart compile exe bin/main.dart -o build/device_doctor - -cp -f LICENSE build/ - -if [[ $OS == "Darwin" ]]; then - mkdir -p build/tool - cp -rf tool/infra-dialog build/tool/ -fi - -popd diff --git a/cipd_packages/device_doctor/tool/ensure_file b/cipd_packages/device_doctor/tool/ensure_file deleted file mode 100644 index 8efdd5027..000000000 --- a/cipd_packages/device_doctor/tool/ensure_file +++ /dev/null @@ -1,3 +0,0 @@ -$ServiceURL https://chrome-infra-packages.appspot.com/ - -dart/dart-sdk/${os}-${arch} dev diff --git a/cipd_packages/device_doctor/tool/ensure_file_windows b/cipd_packages/device_doctor/tool/ensure_file_windows deleted file mode 100644 index 8efdd5027..000000000 --- a/cipd_packages/device_doctor/tool/ensure_file_windows +++ /dev/null @@ -1,3 +0,0 @@ -$ServiceURL https://chrome-infra-packages.appspot.com/ - -dart/dart-sdk/${os}-${arch} dev diff --git a/cipd_packages/device_doctor/tool/infra-dialog/.gitignore b/cipd_packages/device_doctor/tool/infra-dialog/.gitignore deleted file mode 100644 index 14ce7f3cb..000000000 --- a/cipd_packages/device_doctor/tool/infra-dialog/.gitignore +++ /dev/null @@ -1 +0,0 @@ -**/xcuserdata/ diff --git a/cipd_packages/device_doctor/tool/infra-dialog/infra-dialog.xcodeproj/project.pbxproj b/cipd_packages/device_doctor/tool/infra-dialog/infra-dialog.xcodeproj/project.pbxproj deleted file mode 100644 index 1a16d327d..000000000 --- a/cipd_packages/device_doctor/tool/infra-dialog/infra-dialog.xcodeproj/project.pbxproj +++ /dev/null @@ -1,307 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 50; - objects = { - -/* Begin PBXBuildFile section */ - 0374DEB224463F940008623E /* infra_dialogUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0374DEB124463F940008623E /* infra_dialogUITests.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXFileReference section */ - 0374DEAD24463F940008623E /* infra-dialogUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "infra-dialogUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; - 0374DEB124463F940008623E /* infra_dialogUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = infra_dialogUITests.swift; sourceTree = ""; }; - 0374DEB324463F940008623E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 0374DEAA24463F940008623E /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 0374DE8E24463F930008623E = { - isa = PBXGroup; - children = ( - 0374DEB024463F940008623E /* infra-dialogUITests */, - 0374DE9824463F930008623E /* Products */, - ); - sourceTree = ""; - }; - 0374DE9824463F930008623E /* Products */ = { - isa = PBXGroup; - children = ( - 0374DEAD24463F940008623E /* infra-dialogUITests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - 0374DEB024463F940008623E /* infra-dialogUITests */ = { - isa = PBXGroup; - children = ( - 0374DEB124463F940008623E /* infra_dialogUITests.swift */, - 0374DEB324463F940008623E /* Info.plist */, - ); - path = "infra-dialogUITests"; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 0374DEAC24463F940008623E /* infra-dialogUITests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 0374DEB924463F940008623E /* Build configuration list for PBXNativeTarget "infra-dialogUITests" */; - buildPhases = ( - 0374DEA924463F940008623E /* Sources */, - 0374DEAA24463F940008623E /* Frameworks */, - 0374DEAB24463F940008623E /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = "infra-dialogUITests"; - productName = "infra-dialogUITests"; - productReference = 0374DEAD24463F940008623E /* infra-dialogUITests.xctest */; - productType = "com.apple.product-type.bundle.ui-testing"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 0374DE8F24463F930008623E /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 1130; - LastUpgradeCheck = 1320; - TargetAttributes = { - 0374DEAC24463F940008623E = { - CreatedOnToolsVersion = 11.3; - }; - }; - }; - buildConfigurationList = 0374DE9224463F930008623E /* Build configuration list for PBXProject "infra-dialog" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 0374DE8E24463F930008623E; - productRefGroup = 0374DE9824463F930008623E /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 0374DEAC24463F940008623E /* infra-dialogUITests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 0374DEAB24463F940008623E /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 0374DEA924463F940008623E /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 0374DEB224463F940008623E /* infra_dialogUITests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin XCBuildConfiguration section */ - 0374DEB424463F940008623E /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - 0374DEB524463F940008623E /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - SDKROOT = iphoneos; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 0374DEBA24463F940008623E /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = S8QB4VV633; - INFOPLIST_FILE = "infra-dialogUITests/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = "dev.flutter.infra-dialogUITests"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 0374DEBB24463F940008623E /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = S8QB4VV633; - INFOPLIST_FILE = "infra-dialogUITests/Info.plist"; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - PRODUCT_BUNDLE_IDENTIFIER = "dev.flutter.infra-dialogUITests"; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 0374DE9224463F930008623E /* Build configuration list for PBXProject "infra-dialog" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 0374DEB424463F940008623E /* Debug */, - 0374DEB524463F940008623E /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 0374DEB924463F940008623E /* Build configuration list for PBXNativeTarget "infra-dialogUITests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 0374DEBA24463F940008623E /* Debug */, - 0374DEBB24463F940008623E /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 0374DE8F24463F930008623E /* Project object */; -} diff --git a/cipd_packages/device_doctor/tool/infra-dialog/infra-dialog.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/cipd_packages/device_doctor/tool/infra-dialog/infra-dialog.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 2206c75a7..000000000 --- a/cipd_packages/device_doctor/tool/infra-dialog/infra-dialog.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/cipd_packages/device_doctor/tool/infra-dialog/infra-dialog.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/cipd_packages/device_doctor/tool/infra-dialog/infra-dialog.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003..000000000 --- a/cipd_packages/device_doctor/tool/infra-dialog/infra-dialog.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/cipd_packages/device_doctor/tool/infra-dialog/infra-dialog.xcodeproj/xcshareddata/xcschemes/infra-dialog.xcscheme b/cipd_packages/device_doctor/tool/infra-dialog/infra-dialog.xcodeproj/xcshareddata/xcschemes/infra-dialog.xcscheme deleted file mode 100644 index d75446222..000000000 --- a/cipd_packages/device_doctor/tool/infra-dialog/infra-dialog.xcodeproj/xcshareddata/xcschemes/infra-dialog.xcscheme +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - diff --git a/cipd_packages/device_doctor/tool/infra-dialog/infra-dialogUITests/Info.plist b/cipd_packages/device_doctor/tool/infra-dialog/infra-dialogUITests/Info.plist deleted file mode 100644 index 64d65ca49..000000000 --- a/cipd_packages/device_doctor/tool/infra-dialog/infra-dialogUITests/Info.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - - diff --git a/cipd_packages/device_doctor/tool/infra-dialog/infra-dialogUITests/infra_dialogUITests.swift b/cipd_packages/device_doctor/tool/infra-dialog/infra-dialogUITests/infra_dialogUITests.swift deleted file mode 100644 index 077ebf21c..000000000 --- a/cipd_packages/device_doctor/tool/infra-dialog/infra-dialogUITests/infra_dialogUITests.swift +++ /dev/null @@ -1,47 +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 XCTest -import os - -// An XCUITest to dismiss System Dialogs. -class infra_dialogUITests: XCTestCase { - - override func setUp() { - // Stop immediately when a failure occurs. - continueAfterFailure = false - super.setUp() - } - - func testDismissDialogs() { - // Dismiss system dialogs, e.g. No SIM Card Installed - let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard") - let defaultLog = Logger() - - // If the device has low battery or bad cable, report as infra failure. - let failureTexts = ["Low Battery", "This accessory may not be supported"] - let buttonTexts = ["OK", "Later", "Allow", "Remind Me Later", "Close", "Dismiss"] - - // Sometimes a second dialog pops up when one is closed, so let's run 3 times. - for _ in 0..<3 { - for failureText in failureTexts { - let predicate = NSPredicate(format: "label CONTAINS[c] %@", failureText) - let elementQuery = springboard.staticTexts.containing(predicate) - XCTAssertEqual(elementQuery.count, 0); - } - for buttonText in buttonTexts { - let button = springboard.buttons[buttonText] - if button.exists { - defaultLog.log("Found button \(buttonText, privacy: .public)") - defaultLog.log("\(springboard.debugDescription, privacy: .public)") - button.tap() - } - } - } - if springboard.buttons.count > 0 { - defaultLog.log("Unexpected SpringBoard button found") - defaultLog.log("\(springboard.debugDescription, privacy: .public)") - } - } -} diff --git a/cipd_packages/doxygen/.gitignore b/cipd_packages/doxygen/.gitignore deleted file mode 100644 index 58b971bec..000000000 --- a/cipd_packages/doxygen/.gitignore +++ /dev/null @@ -1,12 +0,0 @@ -# CIPD and Dart-SDK binaries -.cipd/ -dart-sdk/ - -# Build outputs -.dart_tool/ -build/ -doxygen_src/ -.ssh/ - -# Dart -.packages diff --git a/cipd_packages/doxygen/LICENSE b/cipd_packages/doxygen/LICENSE deleted file mode 100644 index d5384ca42..000000000 --- a/cipd_packages/doxygen/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/cipd_packages/doxygen/README.md b/cipd_packages/doxygen/README.md deleted file mode 100644 index 13f4e8035..000000000 --- a/cipd_packages/doxygen/README.md +++ /dev/null @@ -1,47 +0,0 @@ -# Doxygen - -This utility tool is used by LUCI infrastructure to manage the CIPD package -for [Doxygen](https://www.doxygen.nl/). - -It offers support for linux only at this time. - -## Dependencies - -Building requires an installation of relatively new versions of `bison` and `yacc`, -as well as `cmake`, `make` and a C++ compiler toolchain. - -## Building - -Doxygen is meant to be distributed via CIPD. - -To create the CIPD package, first make sure that the `build/` and `doxygen_src/` -folders do not exist. - -The build script will download a specific version of Doxygen from the GitHub -page of [Doxygen releases](https://github.com/doxygen/doxygen/tags). - -Which version it downloads can be changed by changing the RELEASE variable -inside of the build script. - -### Auto build - -Every new commit will trigger pre-submit builders to auto build a new version -for different platforms without any tag/ref. - -When a new commit is submitted, post-submit builders will trigger a new version -with a tag of `commit_sha`, and a ref of `staging`. - -### Manual build - -Running `tool/build.sh` will build an executable binary in the `build/bin` folder. -Then push to cipd by running - -```bash -cipd create -in build \ - -name flutter/doxygen/-amd64 \ - -ref \ - -tag sha_timestamp:_ -``` - -* os: `linux`, `mac`, or `windows`. -* ref: `release` or `staging` diff --git a/cipd_packages/doxygen/tool/build.sh b/cipd_packages/doxygen/tool/build.sh deleted file mode 100755 index ba34a1209..000000000 --- a/cipd_packages/doxygen/tool/build.sh +++ /dev/null @@ -1,67 +0,0 @@ -#!/usr/bin/env bash -# 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. - -# Fetches Doxygen from GitHub, and builds it from source. -# -# This currently supports linux and mac. - -set -e - -command -v cipd > /dev/null || { - echo "Please install CIPD (available from depot_tools) and add to path first."; - exit -1; -} - -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd)" -BUILD_DIR="$( dirname "$DIR" )/build" -SRC_DIR="$( dirname "$DIR" )/doxygen_src" -OS="`uname`" -RELEASE="Release_1_9_7" -if [[ $OS == "Darwin" ]]; then - NUM_CPUS=$(sysctl -n hw.ncpu) -else - NUM_CPUS=$(grep -c processor /proc/cpuinfo) -fi - -function fetch_doxygen() ( - cd "$SRC_DIR" - wget -O doxygen.tar.gz "https://github.com/doxygen/doxygen/archive/refs/tags/${RELEASE}.tar.gz" - tar xf doxygen.tar.gz - rm -f doxygen.tar.gz - mv "doxygen-$RELEASE"/* "doxygen-$RELEASE"/.??* . - rm -rf "doxygen-$RELEASE" -) - -function build_doxygen() ( - local bison_opt= - if [[ $OS == "Darwin" ]]; then - # On macOS, doxygen needs an updated version of bison to build with. - bison_opt="-DBISON_EXECUTABLE=/opt/homebrew/opt/bison/bin/bison" - fi - - cd "$SRC_DIR" - cmake "-DCMAKE_INSTALL_PREFIX=$BUILD_DIR" $bison_opt - make -j$NUM_CPUS install -) - -function setup() ( - cd "$DIR/.." - pwd - ls tool - if [[ -d "$BUILD_DIR" ]]; then - echo "Please remove the build directory '$BUILD_DIR' before proceeding" - exit -1 - fi - mkdir -p "$BUILD_DIR" - if [[ -d "$SRC_DIR" ]]; then - echo "Please remove the downloaded source directory '$SRC_DIR' before proceeding" - exit -1 - fi - mkdir -p "$SRC_DIR" -) - -setup -fetch_doxygen -build_doxygen diff --git a/cipd_packages/ruby/LICENSE b/cipd_packages/ruby/LICENSE deleted file mode 100644 index d5384ca42..000000000 --- a/cipd_packages/ruby/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/cipd_packages/ruby/README b/cipd_packages/ruby/README deleted file mode 100644 index 89c29c90d..000000000 --- a/cipd_packages/ruby/README +++ /dev/null @@ -1,19 +0,0 @@ -# Ruby at Flutter - -The scripts in this folder are used to build and package -ruby as a self contained CIPD package. This package is intended -for flutter/flutter repository testing. - -# Updating to new ruby versions. - -Update `ruby_metadata.txt` with the major version -and the file name. E.g. `3.1,ruby-3.1.4.tar.gz` to -change the version of ruby to build. - -# Installing cocoapods. - -The CIPD package will bundle cocoapods with its corresponding -wrapper scripts. - -Update `cococapods_version.txt` with the version of cocoapods -to use. diff --git a/cipd_packages/ruby/cocoapods_version.txt b/cipd_packages/ruby/cocoapods_version.txt deleted file mode 100644 index feaae22ba..000000000 --- a/cipd_packages/ruby/cocoapods_version.txt +++ /dev/null @@ -1 +0,0 @@ -1.13.0 diff --git a/cipd_packages/ruby/ruby_metadata.txt b/cipd_packages/ruby/ruby_metadata.txt deleted file mode 100644 index ccf28966b..000000000 --- a/cipd_packages/ruby/ruby_metadata.txt +++ /dev/null @@ -1 +0,0 @@ -3.1,ruby-3.1.3.tar.gz diff --git a/cipd_packages/ruby/third_party/ruby_ship/LICENSE.txt b/cipd_packages/ruby/third_party/ruby_ship/LICENSE.txt deleted file mode 100644 index 6d4221b59..000000000 --- a/cipd_packages/ruby/third_party/ruby_ship/LICENSE.txt +++ /dev/null @@ -1,22 +0,0 @@ -Copyright (c) 2015 Stephan Nordnes Eriksen - -MIT License - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/cipd_packages/ruby/third_party/ruby_ship/README b/cipd_packages/ruby/third_party/ruby_ship/README deleted file mode 100644 index 87b09d2fd..000000000 --- a/cipd_packages/ruby/third_party/ruby_ship/README +++ /dev/null @@ -1,2 +0,0 @@ -Scripts in this directory are copied from -https://github.com/stephan-nordnes-eriksen/ruby_ship/. diff --git a/cipd_packages/ruby/third_party/ruby_ship/auto_relink_dylibs.rb b/cipd_packages/ruby/third_party/ruby_ship/auto_relink_dylibs.rb deleted file mode 100644 index a27243245..000000000 --- a/cipd_packages/ruby/third_party/ruby_ship/auto_relink_dylibs.rb +++ /dev/null @@ -1,67 +0,0 @@ -require "fileutils" - - - -@current_dir = File.expand_path(File.dirname(__FILE__)) -@new_dylib_path = File.join(@current_dir, "..", "bin", "darwin_ruby", "dylibs") - -FileUtils.mkdir_p(@new_dylib_path) - -def fix_dylib_for_file(file) - results = `otool -L "#{file}"` #Will get information about which dylibs to link - - if results.is_a?(String) && results != "" && !results.include?("is not an object file") && !results.include?("Assertion failed:") - # puts "---------------------------------" - puts "--RELINKING--: #{file}" - # puts "---------------------------------" - expanded = File.expand_path(file) - - #Setting new ID for this if needed - id_path = File.join("darwin_ruby", "dylibs", File.split(expanded)[-1]) - id_reset_results = `install_name_tool -id #{id_path} #{file} 2> /dev/null` #Will link the current file to itself - - lines = results.split("\n") - lines = [] unless lines - itterate = lines[1..-1] - itterate = [] unless itterate - - - itterate.each do |libfile_line| - libfile = libfile_line.split(" (compatibility version")[0].strip - libfile = libfile.split("(")[0] - - next if libfile.include?(@new_dylib_path) # We have already fixed this one - next if libfile.include?("/usr/lib/") # These are global and assumed to be present on all versions of osx - next if libfile.include?("/System/Library/") # Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation - - new_libfile = File.join(@new_dylib_path, File.split(libfile)[-1]) - - unless File.file?(new_libfile) - FileUtils.copy(libfile, new_libfile) - puts "--COPIED-- #{new_libfile}" - fix_dylib_for_file(new_libfile) - end - - relink_command_results = `install_name_tool -change #{libfile} #{new_libfile} #{file} 2> /dev/null` # Will relink external library - puts "Linked: #{new_libfile} to #{file}" - - if relink_command_results != "" && !relink_command_results.include?("error:") - puts "relinked in #{file} link: #{libfile} to #{new_libfile}" - end - end - end -end - -puts "Relinking files from: #{File.dirname(File.expand_path($0))}" - -folders = [File.expand_path(File.join(File.dirname(File.expand_path($0)), "..", "bin", "darwin_ruby"))] - -full = folders.map{|f| Dir[File.join(f, '**', '*')]} -actual_files = full.flatten(1).uniq.select{|e| File.file? e} - - -actual_files.each do |file| - fix_dylib_for_file(file) -end - -puts "Relinking of dylib done" diff --git a/cipd_packages/ruby/third_party/ruby_ship/ruby_ship_build.sh b/cipd_packages/ruby/third_party/ruby_ship/ruby_ship_build.sh deleted file mode 100644 index 439bbcd90..000000000 --- a/cipd_packages/ruby/third_party/ruby_ship/ruby_ship_build.sh +++ /dev/null @@ -1,261 +0,0 @@ -#!/usr/bin/env bash - -set -e -set -x - -function remove_dylib_signatures() { - if [[ -z "$1" ]]; then - echo 'remove_dylib_signatures must be called with the path to the dylibs dir as an argument' >&2 - exit 42 - fi - - # Remove signatures - ls "$1/"* | xargs codesign --remove-signature; - # Resign with adhoc - ls "$1/"* | xargs codesign --force -s -; -} - -# Verify parameters. -if [ $# -eq 0 ]; then - echo "No arguments supplied" - echo "Usage: ruby_build.sh /path/to/ruby_source.tar.gz" - exit 1 -fi - -# This script supports only Mac. -OS="darwin" - -# Directory of this script -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -COCOAPODS_VERSION=$(cat $DIR/../cocoapods_version.txt) - -echo "Compiling and installing ruby" - -# Pre install/cleanup -rm -rf $DIR/../cleanup/extracted_ruby -mkdir $DIR/../cleanup/extracted_ruby - -# Unzip source code. -tar -xzf $1 -C $DIR/../cleanup/extracted_ruby - -#Get ruby version and directory. -RUBYDIR="$(ls $DIR/../cleanup/extracted_ruby)" -RUBY_VERSION="$(echo $RUBYDIR | cut -d'-' -f 2)" - -echo "############################" -echo "Installing ruby version $RUBY_VERSION" -echo "############################" - -# Building ruby. -cd $DIR/../cleanup/extracted_ruby/$RUBYDIR -if [[ "$OS" == "darwin" ]]; then - OPTS="" - OPTS+="$(brew --prefix openssl)" - OPTS+=":$(brew --prefix readline)" - OPTS+=":$(brew --prefix libyaml)" - OPTS+=":$(brew --prefix gdbm)" - OPTS+=":$(brew --prefix libffi)" - $DIR/../cleanup/extracted_ruby/$RUBYDIR/configure \ - --enable-load-relative \ - --prefix=$DIR/../build/bin/${OS}_ruby \ - --with-opt-dir="$OPTS" -fi -make -make install - -# Copy default libraries to build directory -mkdir -p "$DIR/../build/bin/darwin_ruby/dylibs/" -find $(brew --prefix curl)/lib/ -name '*.dylib' -exec cp {} -f "$DIR/../build/bin/darwin_ruby/dylibs/" \; -find $(brew --prefix gdbm)/lib/ -name '*.dylib' -exec cp {} -f "$DIR/../build/bin/darwin_ruby/dylibs/" \; -find $(brew --prefix libffi)/lib/ -name '*.dylib' -exec cp {} -f "$DIR/../build/bin/darwin_ruby/dylibs/" \; -find $(brew --prefix libyaml)/lib/ -name '*.dylib' -exec cp {} -f "$DIR/../build/bin/darwin_ruby/dylibs/" \; -find $(brew --prefix openldap)/lib/ -name '*.dylib' -exec cp {} -f "$DIR/../build/bin/darwin_ruby/dylibs/" \; -find $(brew --prefix openssl)/lib/ -name '*.dylib' -exec cp {} -f "$DIR/../build/bin/darwin_ruby/dylibs/" \; -find $(brew --prefix readline)/lib/ -name '*.dylib' -exec cp {} -f "$DIR/../build/bin/darwin_ruby/dylibs/" \; - -# Setting up reference directories. -RUBY_INSTALL_DIR="$(ls $DIR/../build/bin/${OS}_ruby/include)" -RUBY_VERSION_DIR="$(echo $RUBY_INSTALL_DIR | cut -d'-' -f 2)" -RUBY_BINARY_INSTALL_DIR="$(ls $DIR/../build/bin/${OS}_ruby/lib/ruby/$RUBY_VERSION_DIR | grep ${OS})" - -#SETTING UP COMMON WRAPPER COMPONENTS -OS_SELECTOR=$'#!/usr/bin/env bash\nOS=\"darwin\"\n' -DIR_SETTER="DIR=\"\$( cd \"\$( dirname \"\${BASH_SOURCE[0]}\" )\" && pwd )\"" - - - - -#Building wrappers. - -#ruby -echo "$OS_SELECTOR" > $DIR/../build/bin/ruby -echo "$DIR_SETTER" >> $DIR/../build/bin/ruby -echo "bash -e \"\${DIR}/\${OS}_ruby/\${OS}_ruby.sh\" \"\$@\"" >> $DIR/../build/bin/ruby - -#gem -echo "$OS_SELECTOR" > $DIR/../build/bin/gem -echo "$DIR_SETTER" >> $DIR/../build/bin/gem -echo "bash -e \"\${DIR}/\${OS}_ruby/\${OS}_gem.sh\" \"\$@\"" >> $DIR/../build/bin/gem - -#erb -echo "$OS_SELECTOR" > $DIR/../build/bin/erb -echo "$DIR_SETTER" >> $DIR/../build/bin/erb -echo "bash -e \"\${DIR}/\${OS}_ruby/\${OS}_erb.sh\" \"\$@\"" >> $DIR/../build/bin/erb - -#irb -echo "$OS_SELECTOR" > $DIR/../build/bin/irb -echo "$DIR_SETTER" >> $DIR/../build/bin/irb -echo "bash -e \"\${DIR}/\${OS}_ruby/\${OS}_irb.sh\" \"\$@\"" >> $DIR/../build/bin/irb - -#rake -echo "$OS_SELECTOR" > $DIR/../build/bin/rake -echo "$DIR_SETTER" >> $DIR/../build/bin/rake -echo "bash -e \"\${DIR}/\${OS}_ruby/\${OS}_rake.sh\" \"\$@\"" >> $DIR/../build/bin/rake - -#rdoc -echo "$OS_SELECTOR" > $DIR/../build/bin/rdoc -echo "$DIR_SETTER" >> $DIR/../build/bin/rdoc -echo "bash -e \"\${DIR}/\${OS}_ruby/\${OS}_rdoc.sh\" \"\$@\"" >> $DIR/../build/bin/rdoc - -#ri -echo "$OS_SELECTOR" > $DIR/../build/bin/ri -echo "$DIR_SETTER" >> $DIR/../build/bin/ri -echo "bash -e \"\${DIR}/\${OS}_ruby/\${OS}_ri.sh\" \"\$@\"" >> $DIR/../build/bin/ri - -#bundle -echo "$OS_SELECTOR" > $DIR/../build/bin/bundle -echo "$DIR_SETTER" >> $DIR/../build/bin/bundle -echo "bash -e \"\${DIR}/\${OS}_ruby/\${OS}_ri.sh\" \"\$@\"" >> $DIR/../build/bin/bundle - -#bundler -echo "$OS_SELECTOR" > $DIR/../build/bin/bundler -echo "$DIR_SETTER" >> $DIR/../build/bin/bundler -echo "bash -e \"\${DIR}/\${OS}_ruby/\${OS}_bundler.sh\" \"\$@\"" >> $DIR/../build/bin/bundler - -#pod -echo "$OS_SELECTOR" > $DIR/../build/bin/pod -echo "$DIR_SETTER" >> $DIR/../build/bin/pod -echo "bash -e \"\${DIR}/\${OS}_ruby/\${OS}_pod.sh\" \"\$@\"" >> $DIR/../build/bin/pod - - -# Making OS specific scripts: - -GEM_PATH_SETTER="GEM_PATH=\"\${DIR}/lib/ruby/gems/$RUBY_VERSION_DIR/:\${DIR}/lib/ruby/$RUBY_VERSION_DIR/:\${DIR}/bin/:\${DIR}/lib/ruby/$RUBY_VERSION_DIR/$RUBY_BINARY_INSTALL_DIR/\"" -GEM_HOME_SETTER="GEM_HOME=\"\${DIR}/lib/ruby/gems/$RUBY_VERSION_DIR/\"" - -# OS_ruby -echo "$DIR_SETTER" > $DIR/../build/bin/${OS}_ruby/${OS}_ruby.sh -echo "$GEM_PATH_SETTER" >> $DIR/../build/bin/${OS}_ruby/${OS}_ruby.sh -echo "$GEM_HOME_SETTER" >> $DIR/../build/bin/${OS}_ruby/${OS}_ruby.sh -echo "\"\${DIR}/bin/ruby\" -I \"\${DIR}/lib/ruby/gems/$RUBY_VERSION_DIR/\" -I \"\${DIR}/lib/ruby/$RUBY_VERSION_DIR/\" -I \"\${DIR}/bin/\" -I \"\${DIR}/lib/ruby/$RUBY_VERSION_DIR/$RUBY_BINARY_INSTALL_DIR/\"" \"\$@\" >> $DIR/../build/bin/${OS}_ruby/${OS}_ruby.sh - -# gem command script: -echo "$DIR_SETTER" > $DIR/../build/bin/${OS}_ruby/${OS}_gem.sh -echo "$GEM_PATH_SETTER" >> $DIR/../build/bin/${OS}_ruby/${OS}_gem.sh -echo "$GEM_HOME_SETTER" >> $DIR/../build/bin/${OS}_ruby/${OS}_gem.sh -echo "\"\${DIR}/bin/gem\" \"\$@\"" >> $DIR/../build/bin/${OS}_ruby/${OS}_gem.sh -echo "if [[ \"\$1\" == \"install\" ]]; then" >> $DIR/../build/bin/${OS}_ruby/${OS}_gem.sh -echo "\"${DIR}/../build/bin/ruby\" \"\${DIR}/../../tools/auto_relink_dylibs.rb\"" >> $DIR/../build/bin/${OS}_ruby/${OS}_gem.sh -echo "fi" >> $DIR/../build/bin/${OS}_ruby/${OS}_gem.sh - -# erb command script: -echo "$DIR_SETTER" > $DIR/../build/bin/${OS}_ruby/${OS}_erb.sh -echo "$GEM_PATH_SETTER" >> $DIR/../build/bin/${OS}_ruby/${OS}_erb.sh -echo "$GEM_HOME_SETTER" >> $DIR/../build/bin/${OS}_ruby/${OS}_erb.sh -echo "\"\${DIR}/bin/erb\" \"\$@\"" >> $DIR/../build/bin/${OS}_ruby/${OS}_erb.sh - -# irb command script: -echo "$DIR_SETTER" > $DIR/../build/bin/${OS}_ruby/${OS}_irb.sh -echo "$GEM_PATH_SETTER" >> $DIR/../build/bin/${OS}_ruby/${OS}_irb.sh -echo "$GEM_HOME_SETTER" >> $DIR/../build/bin/${OS}_ruby/${OS}_irb.sh -echo "\"\${DIR}/bin/irb\" \"\$@\"" >> $DIR/../build/bin/${OS}_ruby/${OS}_irb.sh - -# rake command script: -echo "$DIR_SETTER" > $DIR/../build/bin/${OS}_ruby/${OS}_rake.sh -echo "$GEM_PATH_SETTER" >> $DIR/../build/bin/${OS}_ruby/${OS}_rake.sh -echo "$GEM_HOME_SETTER" >> $DIR/../build/bin/${OS}_ruby/${OS}_rake.sh -echo "\"\${DIR}/bin/rake\" \"\$@\"" >> $DIR/../build/bin/${OS}_ruby/${OS}_rake.sh - -# rdoc command script: -echo "$DIR_SETTER" > $DIR/../build/bin/${OS}_ruby/${OS}_rdoc.sh -echo "$GEM_PATH_SETTER" >> $DIR/../build/bin/${OS}_ruby/${OS}_rdoc.sh -echo "$GEM_HOME_SETTER" >> $DIR/../build/bin/${OS}_ruby/${OS}_rdoc.sh -echo "\"\${DIR}/bin/rdoc\" \"\$@\"" >> $DIR/../build/bin/${OS}_ruby/${OS}_rdoc.sh - -#ri command script: -echo "$DIR_SETTER" > $DIR/../build/bin/${OS}_ruby/${OS}_ri.sh -echo "$GEM_PATH_SETTER" >> $DIR/../build/bin/${OS}_ruby/${OS}_ri.sh -echo "$GEM_HOME_SETTER" >> $DIR/../build/bin/${OS}_ruby/${OS}_ri.sh -echo "\"\${DIR}/bin/ri\" \"\$@\"" >> $DIR/../build/bin/${OS}_ruby/${OS}_ri.sh - -# pod command script: -echo "$DIR_SETTER" > $DIR/../build/bin/${OS}_ruby/${OS}_pod.sh -echo "$GEM_PATH_SETTER" >> $DIR/../build/bin/${OS}_ruby/${OS}_pod.sh -echo "$GEM_HOME_SETTER" >> $DIR/../build/bin/${OS}_ruby/${OS}_pod.sh -echo "\"\${DIR}/bin/pod\" \"\$@\"" >> $DIR/../build/bin/${OS}_ruby/${OS}_pod.sh - - -# bundle command script: -echo "$DIR_SETTER" > $DIR/../build/bin/${OS}_ruby/${OS}_bundle.sh -echo "$GEM_PATH_SETTER" >> $DIR/../build/bin/${OS}_ruby/${OS}_bundle.sh -echo "$GEM_HOME_SETTER" >> $DIR/../build/bin/${OS}_ruby/${OS}_bundle.sh -echo "\"\${DIR}/../build/bin/${OS}_ruby/lib/ruby/gems/$RUBY_VERSION_DIR/bin/bundle\" \"\$@\"" >> $DIR/../build/bin/${OS}_ruby/${OS}_bundle.sh -echo "if [ \"\$1\" == \"install\" ] || [ \"\$1\" == \"update\" ]; then" >> $DIR/../build/bin/${OS}_ruby/${OS}_bundle.sh -echo " ${DIR}/../build/bin/ruby \"\${DIR}/../../auto_relink_dylibs.rb\"" >> $DIR/../build/bin/${OS}_ruby/${OS}_bundle.sh -echo "fi" >> $DIR/../build/bin/${OS}_ruby/${OS}_bundle.sh - -#bundler command script: -echo "$DIR_SETTER" > $DIR/../build/bin/${OS}_ruby/${OS}_bundler.sh -echo "$GEM_PATH_SETTER" >> $DIR/../build/bin/${OS}_ruby/${OS}_bundler.sh -echo "$GEM_HOME_SETTER" >> $DIR/../build/bin/${OS}_ruby/${OS}_bundler.sh -echo "\"\${DIR}/bin/bundler\" \"\$@\"" >> $DIR/../build/bin/${OS}_ruby/${OS}_bundler.sh - -chmod a+x $DIR/../build/bin/ruby -chmod a+x $DIR/../build/bin/gem -chmod a+x $DIR/../build/bin/erb -chmod a+x $DIR/../build/bin/irb -chmod a+x $DIR/../build/bin/pod -chmod a+x $DIR/../build/bin/rake -chmod a+x $DIR/../build/bin/rdoc -chmod a+x $DIR/../build/bin/ri -chmod a+x $DIR/../build/bin/bundle -chmod a+x $DIR/../build/bin/bundler - -chmod a+x $DIR/../build/bin/${OS}_ruby/${OS}_ruby.sh -chmod a+x $DIR/../build/bin/${OS}_ruby/${OS}_gem.sh -chmod a+x $DIR/../build/bin/${OS}_ruby/${OS}_erb.sh -chmod a+x $DIR/../build/bin/${OS}_ruby/${OS}_irb.sh -chmod a+x $DIR/../build/bin/${OS}_ruby/${OS}_rake.sh -chmod a+x $DIR/../build/bin/${OS}_ruby/${OS}_rdoc.sh -chmod a+x $DIR/../build/bin/${OS}_ruby/${OS}_ri.sh -chmod a+x $DIR/../build/bin/${OS}_ruby/${OS}_bundle.sh -chmod a+x $DIR/../build/bin/${OS}_ruby/${OS}_bundler.sh -chmod a+x $DIR/../build/bin/${OS}_ruby/${OS}_pod.sh - -# Remove signatures from ruby and re-sign as adhoc. -codesign --remove-signature $DIR/../build/bin/darwin_ruby/bin/ruby -codesign --force -s - $DIR/../build/bin/darwin_ruby/bin/ruby -remove_dylib_signatures "$DIR/../build/bin/darwin_ruby/dylibs" -# Install bundler -$DIR/../build/bin/gem cleanup bundler -$DIR/../build/bin/gem install -f bundler -remove_dylib_signatures "$DIR/../build/bin/darwin_ruby/dylibs" - -# Install cococoapods -$DIR/../build/bin/gem install activesupport -v 7.0.8 # Pin this dep version. -$DIR/../build/bin/gem install cocoapods -v $COCOAPODS_VERSION -remove_dylib_signatures "$DIR/../build/bin/darwin_ruby/dylibs" - -# Cleanup temp folder. -rm -rf $DIR/../cleanup - - -#NOTIFY USER ON HOW TO USE RUBY SHIP -echo "############################" -echo "############DONE############" -echo "############################" -echo "Finished creating bundler for Ruby $RUBY_VERSION!" -echo "Run scripts by using scripts in the bin/ as you would use the normal ruby command." -echo "Eg.: bin/ruby -v" -echo "=> ruby $RUBY_VERSION..." diff --git a/cipd_packages/ruby/tools/build.sh b/cipd_packages/ruby/tools/build.sh deleted file mode 100644 index 733786bc9..000000000 --- a/cipd_packages/ruby/tools/build.sh +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env bash -# 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. - -# Script to build a self contained ruby cipd packages. - -set -e -set -x - -# Get the directory of this script. -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" - -# Get versions from config files. -RUBY_MAJOR_VERSION=$(cat $DIR/../ruby_metadata.txt | cut -d ',' -f 1) -RUBY_FILE_NAME=$(cat $DIR/../ruby_metadata.txt | cut -d ',' -f 2) - - -# Create the package structure. -rm -rf $DIR/../build && mkdir -p $DIR/../build -mkdir -p $DIR/../build/tools - -# Copy files to build directory. -cp $DIR/../third_party/ruby_ship/auto_relink_dylibs.rb $DIR/../build/tools/auto_relink_dylibs.rb -cp $DIR/../third_party/ruby_ship/ruby_ship_build.sh $DIR/ruby_build.sh -cp $DIR/../LICENSE $DIR/../build/LICENSE - -# Download ruby. -mkdir -p $DIR/../cleanup -curl https://cache.ruby-lang.org/pub/ruby/$RUBY_MAJOR_VERSION/$RUBY_FILE_NAME -o $DIR/../cleanup/$RUBY_FILE_NAME - -# Install brew dependencies -brew install curl -brew install openldap -brew reinstall --build-from-source gdbm -brew reinstall --build-from-source gmp -brew reinstall --build-from-source libffi -brew reinstall libyaml -brew reinstall --build-from-source readline -brew reinstall --build-from-source openssl@3 -brew reinstall --build-from-source m4 - -bash -e $DIR/ruby_build.sh $DIR/../cleanup/$RUBY_FILE_NAME - -# Copy certificates bundle for mac x64. -if [ -f /usr/local/etc/ca-certificates/cert.pem ]; then - cp /usr/local/etc/ca-certificates/cert.pem $DIR/../build/bin/darwin_ruby/ -fi; - -# Copy certificates bundle for mac x64. -if [ -f /opt/homebrew/etc/openssl@3/cert.pem ]; then - cp /opt/homebrew/etc/openssl@3/cert.pem $DIR/../build/bin/darwin_ruby/ -fi; - -# Update wrapper scripts to make them use libraries from new location. -sed -i'' -e 's/bindir="\${0%\/\*}"/&\nSSL_CERTS="\$( cd -- "\$(dirname "\$0")" >\/dev\/null 2>\&1 ; pwd -P )\/..\/cert.pem"\nLIBSPATH="\$( cd -- "\$(dirname "\$0")" >\/dev\/null 2>\&1 ; pwd -P )\/..\/dylibs"\nexport DYLD_FALLBACK_LIBRARY_PATH=\$LIBSPATH:\$DYLD_FALLBACK_LIBRARY_PATH\nexport SSL_CERT_FILE="\${SSL_CERTS}"/' $DIR/../build/bin/darwin_ruby/bin/gem -sed -i'' -e 's/bindir="\${0%\/\*}"/&\nSSL_CERTS="\$( cd -- "\$(dirname "\$0")" >\/dev\/null 2>\&1 ; pwd -P )\/..\/cert.pem"\nLIBSPATH="\$( cd -- "\$(dirname "\$0")" >\/dev\/null 2>\&1 ; pwd -P )\/..\/dylibs"\nexport DYLD_FALLBACK_LIBRARY_PATH=\$LIBSPATH:\$DYLD_FALLBACK_LIBRARY_PATH\nexport SSL_CERT_FILE="\${SSL_CERTS}"/' $DIR/../build/bin/darwin_ruby/bin/bundler -sed -i'' -e 's/bindir="\${0%\/\*}"/&\nSSL_CERTS="\$( cd -- "\$(dirname "\$0")" >\/dev\/null 2>\&1 ; pwd -P )\/..\/cert.pem"\nLIBSPATH="\$( cd -- "\$(dirname "\$0")" >\/dev\/null 2>\&1 ; pwd -P )\/..\/dylibs"\nexport DYLD_FALLBACK_LIBRARY_PATH=\$LIBSPATH:\$DYLD_FALLBACK_LIBRARY_PATH\nexport SSL_CERT_FILE="\${SSL_CERTS}"/' $DIR/../build/bin/darwin_ruby/bin/bundle -sed -i'' -e 's/bindir="\${0%\/\*}"/&\nSSL_CERTS="\$( cd -- "\$(dirname "\$0")" >\/dev\/null 2>\&1 ; pwd -P )\/..\/cert.pem"\nLIBSPATH="\$( cd -- "\$(dirname "\$0")" >\/dev\/null 2>\&1 ; pwd -P )\/..\/dylibs"\nexport DYLD_FALLBACK_LIBRARY_PATH=\$LIBSPATH:\$DYLD_FALLBACK_LIBRARY_PATH\nexport SSL_CERT_FILE="\${SSL_CERTS}"/' $DIR/../build/bin/darwin_ruby/bin/pod - -# Patch FFI to find libc.dylib in the standard location. -find $DIR/../build -name library.rb -exec sed -i'' -e 's/LIBC = FFI\:\:Platform\:\:LIBC/LIBC = \x27\/usr\/lib\/libc.dylib\x27/' {} \; - -# Ensure all the command are working properly -$DIR/../build/bin/bundle --version -$DIR/../build/bin/bundler --version -$DIR/../build/bin/gem --version -$DIR/../build/bin/pod --version diff --git a/cloud_build/dashboard_build.sh b/cloud_build/dashboard_build.sh deleted file mode 100755 index 988008c13..000000000 --- a/cloud_build/dashboard_build.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/usr/bin/env bash -# 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. - -# Build flutter build dashboard. - -pushd dashboard > /dev/null -set -e -rm -rf build -flutter channel master -flutter upgrade -flutter doctor -flutter pub get -flutter config --enable-web -flutter build web --web-renderer=canvaskit --release -rm -rf ../app_dart/build -cp -r build ../app_dart/build -flutter clean -popd > /dev/null diff --git a/cloud_build/deploy_app_dart.sh b/cloud_build/deploy_app_dart.sh deleted file mode 100755 index 93caba5eb..000000000 --- a/cloud_build/deploy_app_dart.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash -# 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. - -# Deploy a new flutter dashboard version to google cloud. - -pushd app_dart > /dev/null -set -e -# app_dart -gcloud app deploy --project "$1" --version "version-$2" -q --no-promote --no-stop-previous-version --image-url "us-docker.pkg.dev/$1/appengine/default.version-$2" -gcloud app services set-traffic default --splits version-$2=1 --quiet -# Google Cloud quota only allows 30 versions. -# For daily builds, this will keep the 10 most recent versions, but wipe the rest. -gcloud app versions list --format="value(version.id)" --sort-by="~version.createTime" --service=default | tail -n +11 | xargs -r gcloud app versions delete --quiet -popd > /dev/null diff --git a/cloud_build/deploy_auto_submit.sh b/cloud_build/deploy_auto_submit.sh deleted file mode 100755 index b45e72473..000000000 --- a/cloud_build/deploy_auto_submit.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash -# 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. - -# Deploy a new auto submit version to google cloud. - -pushd auto_submit > /dev/null -# auto_submit -gcloud app deploy --project "$1" --version "version-$2" -q --no-promote --no-stop-previous-version --image-url "us-docker.pkg.dev/$1/appengine/auto-submit.version-$2" -gcloud app services set-traffic auto-submit --splits version-$2=1 --quiet -# Google Cloud quota only allows 30 versions. -# For daily builds, this will keep the 10 most recent versions, but wipe the rest. -gcloud app versions list --format="value(version.id)" --sort-by="~version.createTime" --service=auto-submit | tail -n +20 | xargs -r gcloud app versions delete --quiet -popd > /dev/null diff --git a/cloud_build/deploy_cron_jobs.sh b/cloud_build/deploy_cron_jobs.sh deleted file mode 100755 index 5e6ae0806..000000000 --- a/cloud_build/deploy_cron_jobs.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bash -# 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. - -# Deploy latest cron jobs to google cloud. -# This includes cron jobs for both app_dart and auto_submit. - -gcloud app deploy --project "$1" cron.yaml diff --git a/cloud_build/get_docker_image_provenance.sh b/cloud_build/get_docker_image_provenance.sh deleted file mode 100755 index 2d40040ab..000000000 --- a/cloud_build/get_docker_image_provenance.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env bash -# 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. - -# This script is used to pull a docker image's provenance and save it to a file. -DOCKER_IMAGE_URL=$1 -OUTPUT_DIRECTORY=$2 -# Getting the docker image provenance can be flaky, so retry up to 3 times. -MAX_ATTEMPTS=3 - -# Download the jq binary in order to obtain the artifact registry url from the -# docker image provenance. -echo "Installing jq using apt..." -apt update && apt install jq -y - -for attempt in $(seq 1 $MAX_ATTEMPTS) -do - echo "(Attempt $attempt) Obtaining provenance for $1" - gcloud artifacts docker images describe \ - $DOCKER_IMAGE_URL --show-provenance --format json > tmp.json - COMMAND_RESULT=$? - val=$(cat tmp.json | jq -r '.provenance_summary.provenance[0].envelope.payload' | base64 -d | jq '.predicate.recipe.arguments.sourceProvenance') - cat tmp.json | jq ".provenance_summary.provenance[0].build.intotoStatement.slsaProvenance.recipe.arguments.sourceProvenance = ${val}" > $OUTPUT_DIRECTORY - if [[ $COMMAND_RESULT -eq 0 ]] - then - echo "Successfully obtained provenance and saved to $2" - break - else - echo "Failed to obtain provenance." - sleep 30 - fi -done - -if [[ $COMMAND_RESULT -ne 0 ]] -then - echo "Failed to download provenance." && exit $COMMAND_RESULT -fi diff --git a/cloud_build/verify_provenance.sh b/cloud_build/verify_provenance.sh deleted file mode 100755 index 9b11808bf..000000000 --- a/cloud_build/verify_provenance.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env bash -# 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. - -set -e - -# This script is used to verify provenance of our artifacts using slsa-verifier. -# If slsa-verifier is unable to ensure the provenance of the artifact is -# legitimate, then the script will exit with a non-zero exit code. -PROVENANCE_PATH=$1 -BUILDER_ID=https://cloudbuild.googleapis.com/GoogleHostedWorker -SOURCE_URI=github.com/flutter/cocoon - -# Download the jq binary in order to obtain the artifact registry url from the -# docker image provenance. -echo "Installing jq using apt..." -apt update && apt install jq -y - -# Download slsa-verifier in order to validate the docker image provenance. -# This takes the version of slsa-verifier defined in tooling/go.mod. -echo "Installing slsa-verifier using go..." -mkdir -p tooling -pushd tooling -go install github.com/slsa-framework/slsa-verifier/v2/cli/slsa-verifier -popd - -FULLY_QUALIFIED_DIGEST=$(cat $PROVENANCE_PATH | - jq -r .image_summary.fully_qualified_digest) - -# This command uses slsa-verifier to ensure the provenance has the correct -# source location and builder. -# "source-uri" is the original location of the source code -# "builder-id" is where the artifact was built (Note: GoogleHostedWorker is -# a GCP Cloud Build instance) -# -# Note: jq is used in order to obtain the full artifact registry url from -# the provenance metadata. -echo "Verifying the provenance is valid and correct..." -echo "Checking for source-uri of $SOURCE_URI" -slsa-verifier verify-image $FULLY_QUALIFIED_DIGEST \ - --source-uri $SOURCE_URI \ - --builder-id=$BUILDER_ID \ - --provenance-path $PROVENANCE_PATH diff --git a/cloudbuild_cron.yaml b/cloudbuild_cron.yaml deleted file mode 100644 index 3706a8347..000000000 --- a/cloudbuild_cron.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# Provide instructions for google Cloud Build to deploy cron jobs -# to flutter-dashboard project. It will be triggered -# by daily schedule on `main` branch. -# -# The deploy will be skipped if no new commits since last deployment. - -steps: - # 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_cron_jobs.sh $PROJECT_ID - fi - -timeout: 600s diff --git a/cron.yaml b/cron.yaml deleted file mode 100644 index 43e261814..000000000 --- a/cron.yaml +++ /dev/null @@ -1,81 +0,0 @@ -# This config is automatically deployed by cloudbuild/deploy_cron_jobs.sh. -# -# To manually deploy this config, run: -# gcloud app deploy --project flutter-dashboard cron.yaml -cron: -- description: retrieve missing commits - url: /api/vacuum-github-commits - schedule: every 6 hours - -# Disabled for https://github.com/flutter/flutter/issues/121989 -# TODO(keyonghan): will delete if `In Progress` hanging issue is resolved: -# https://github.com/flutter/flutter/issues/120395#issuecomment-1444810718 -#- description: vacuum stale tasks -# url: /api/scheduler/vacuum-stale-tasks -# schedule: every 10 minutes - -- description: backfills builds - url: /api/scheduler/batch-backfiller - schedule: every 5 minutes - -- description: sends build status to GitHub to annotate flutter PRs and commits - url: /api/push-build-status-to-github?repo=flutter/flutter - schedule: every 1 minutes - -- description: sends pr-specific gold status to GitHub to annotate flutter and engine PRs and commits - url: /api/push-gold-status-to-github - schedule: every 5 minutes - -- description: sends build status to GitHub to annotate engine PRs and commits - url: /api/push-build-status-to-github?repo=flutter/engine - schedule: every 2 minutes - -- description: sends build status to GitHub to annotate packages PRs and commits - url: /api/push-build-status-to-github?repo=flutter/packages - schedule: every 2 minutes - -- description: push github rate limit history to bigquery - url: /api/public/github-rate-limit-status - schedule: every 1 minutes - -- description: detect and flag tests with high flaky rates 1 - url: /api/file_flaky_issue_and_pr?threshold=0.02 - schedule: every wednesday 9:00 - -- description: detect and flag tests with high flaky rates 2 - url: /api/file_flaky_issue_and_pr?threshold=0.02 - schedule: every wednesday 9:30 - -- description: detect and flag tests with high flaky rates 3 - url: /api/file_flaky_issue_and_pr?threshold=0.02 - schedule: every wednesday 10:00 - -- description: detect and flag tests with high flaky rates 4 - url: /api/file_flaky_issue_and_pr?threshold=0.02 - schedule: every wednesday 10:30 - -- description: detect and flag tests with high flaky rates 5 - url: /api/file_flaky_issue_and_pr?threshold=0.02 - schedule: every wednesday 11:00 - -- description: update existing flake issues with latest statistics - url: /api/update_existing_flaky_issues?threshold=0.02 - schedule: every wednesday 9:10 - -- description: check flaky builders to either deflake the builder or file a new flaky bug - url: /api/check_flaky_builders - schedule: every wednesday 9:20 - -- description: update branches to reflect most recent commit activity - url: '/api/update-branches' - schedule: every wednesday 19:00 - -- description: check pull requests in the autosubmit bot - url: /check-pull-request - target: auto-submit - schedule: every 1 minutes - -- description: check revert requests in the autosubmit bot - url: /check-revert-requests - target: auto-submit - schedule: every 1 minutes diff --git a/dashboard/.gitignore b/dashboard/.gitignore deleted file mode 100644 index eeac7d10f..000000000 --- a/dashboard/.gitignore +++ /dev/null @@ -1,102 +0,0 @@ -# Miscellaneous -*.class -*.log -*.pyc -*.swp -.DS_Store -.atom/ -.buildlog/ -.history -.svn/ - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# Visual Studio Code related -.classpath -.project -.settings/ -.vscode/ - -# packages file containing multi-root paths -.packages.generated - -# Flutter/Dart/Pub related -**/doc/api/ -.dart_tool/ -.flutter-plugins -.flutter-plugins-dependencies -**/generated_plugin_registrant.dart -.packages -.pub-cache/ -.pub/ -build/ -flutter_*.png -linked_*.ds -unlinked.ds -unlinked_spec.ds - -# Android related -**/android/**/gradle-wrapper.jar -**/android/.gradle -**/android/captures/ -**/android/gradlew -**/android/gradlew.bat -**/android/local.properties -**/android/**/GeneratedPluginRegistrant.java -**/android/key.properties -*.jks - -# iOS/XCode related -**/ios/**/*.mode1v3 -**/ios/**/*.mode2v3 -**/ios/**/*.moved-aside -**/ios/**/*.pbxuser -**/ios/**/*.perspectivev3 -**/ios/**/*sync/ -**/ios/**/.sconsign.dblite -**/ios/**/.tags* -**/ios/**/.vagrant/ -**/ios/**/DerivedData/ -**/ios/**/Icon? -**/ios/**/Pods/ -**/ios/**/.symlinks/ -**/ios/**/profile -**/ios/**/xcuserdata -**/ios/.generated/ -**/ios/Flutter/.last_build_id -**/ios/Flutter/App.framework -**/ios/Flutter/Flutter.framework -**/ios/Flutter/Flutter.podspec -**/ios/Flutter/Generated.xcconfig -**/ios/Flutter/app.flx -**/ios/Flutter/app.zip -**/ios/Flutter/flutter_assets/ -**/ios/Flutter/flutter_export_environment.sh -**/ios/ServiceDefinitions.json -**/ios/Runner/GeneratedPluginRegistrant.* - -# macOS -**/macos/Flutter/GeneratedPluginRegistrant.swift -**/macos/Flutter/Flutter-Debug.xcconfig -**/macos/Flutter/Flutter-Release.xcconfig -**/macos/Flutter/Flutter-Profile.xcconfig - -# Coverage -coverage/ - -# Symbols -app.*.symbols - -# Exceptions to above rules. -!**/ios/**/default.mode1v3 -!**/ios/**/default.mode2v3 -!**/ios/**/default.pbxuser -!**/ios/**/default.perspectivev3 - -# Plugins autogenerated -**/generated_plugin_registrant.cc -**/generated_plugin_registrant.h diff --git a/dashboard/.metadata b/dashboard/.metadata deleted file mode 100644 index edf55eaf8..000000000 --- a/dashboard/.metadata +++ /dev/null @@ -1,45 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: "7e7a0a811a330779fafa19f9b57d931fa3cbbd94" - channel: "master" - -project_type: app - -# Tracks metadata for the flutter migrate command -migration: - platforms: - - platform: root - create_revision: 7e7a0a811a330779fafa19f9b57d931fa3cbbd94 - base_revision: 7e7a0a811a330779fafa19f9b57d931fa3cbbd94 - - platform: android - create_revision: 7e7a0a811a330779fafa19f9b57d931fa3cbbd94 - base_revision: 7e7a0a811a330779fafa19f9b57d931fa3cbbd94 - - platform: ios - create_revision: 7e7a0a811a330779fafa19f9b57d931fa3cbbd94 - base_revision: 7e7a0a811a330779fafa19f9b57d931fa3cbbd94 - - platform: linux - create_revision: 7e7a0a811a330779fafa19f9b57d931fa3cbbd94 - base_revision: 7e7a0a811a330779fafa19f9b57d931fa3cbbd94 - - platform: macos - create_revision: 7e7a0a811a330779fafa19f9b57d931fa3cbbd94 - base_revision: 7e7a0a811a330779fafa19f9b57d931fa3cbbd94 - - platform: web - create_revision: 7e7a0a811a330779fafa19f9b57d931fa3cbbd94 - base_revision: 7e7a0a811a330779fafa19f9b57d931fa3cbbd94 - - platform: windows - create_revision: 7e7a0a811a330779fafa19f9b57d931fa3cbbd94 - base_revision: 7e7a0a811a330779fafa19f9b57d931fa3cbbd94 - - # User provided section - - # List of Local paths (relative to this file) that should be - # ignored by the migrate tool. - # - # Files that are not part of the templates will be ignored by default. - unmanaged_files: - - 'lib/main.dart' - - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/dashboard/LICENSE b/dashboard/LICENSE deleted file mode 100644 index d5384ca42..000000000 --- a/dashboard/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/dashboard/README.md b/dashboard/README.md deleted file mode 100644 index 14e6a5760..000000000 --- a/dashboard/README.md +++ /dev/null @@ -1,111 +0,0 @@ -# Flutter Dashboard - -[User Guide](USER_GUIDE.md) - -## Running for web locally - -`flutter run -d chrome --web-port=8080` - -Must run on port 8080 for Google Sign In to work since that is the -only enabled port for localhost. - -## Running native app locally - -`flutter run -d ` - -If you want to run a debug native app with production data, you can run it like so: - -`flutter run -d -a --use-production-service` - -If you want to run a release native app with fake data, use: - -`flutter run -d --release -a --no-use-production-service` - -[Unfortunately, there's no web equivalent for passing command line arguments yet] - -You can also build a release AOT-compiled desktop app with `flutter build - --release` and then just run the app with the appropriate -argument. - -## Style - -### File organization - -The `lib` directory is organized in subdirectories as follows: - -* `lib/`: main.dart and top-level widgets (e.g. the build dashboard - widget). Can import anything. - -* `lib/widgets/`: Custom widgets used in this project. The files in - this directory should import either the Flutter widgets package or - the Flutter material package, and may import any local files except - those that are directly in the top-level `lib/` directory (i.e. - importing from `lib/logic/` et al is fine). - -* `lib/state/`: States, objects that interface between widgets and the - services. Files in this directory should not import any Flutter - packages other than foundation, and should not import local files - that relate to UI (e.g. those in `widgets/`). - -* `lib/service/`: Services, objects that interface with the network. - Files in this directory should not import any Flutter packages other - than foundation, and should not import any local files from other - directories. - -* `lib/logic/`: Other code, mainly non-UI utility methods and data - models. Files in this directory should not import any Flutter - packages other than foundation, and should not import any local - files from other directories. - -The `test` directory should follow the same structure, with files -testing code from files in `lib` being in the parallel equivalent -directories. - -### Imports - -Imports in Dart files in this project should follow the rules -enforced by the `directives_ordering` lint described -[here](https://dart-lang.github.io/linter/lints/directives_ordering.html). - -## Tests - -### Updating Goldens - -The build dashboard has a few custom render objects to optimize rendering of a large 2D array of rects. - -The tests require a linux host to be updated: - -```sh -flutter test --update-goldens -``` - -## Deploying - -### Web - -Cocoon has a daily Cloud Build trigger that will publish this to -https://flutter-dashboard-appspot.com. - -### Playstore - -#### Set up - -Download signing key from Valentine (under dashboards@flutter.dev). Save to -`$HOME/upload-keystore.jks` - -Create `android/key.properties` - -```sh -storePassword=$password -keyPassword=$password -keyAlias=upload -storeFile=$HOME/upload-keystore.jks -``` - -#### Publishing - -`flutter build appbundle` - -We ship debug mode as it makes it easy to debug issues in production. - -In the Play Console for dashboards@flutter.dev, upload the new app.aab output. \ No newline at end of file diff --git a/dashboard/USER_GUIDE.md b/dashboard/USER_GUIDE.md deleted file mode 100644 index c59092fca..000000000 --- a/dashboard/USER_GUIDE.md +++ /dev/null @@ -1,53 +0,0 @@ -# Flutter Dashboard User Guide - -The dashboard is generated based on a repo's [.ci.yaml](../CI_YAML.md). - -## Build dashboard - -The build page is accessible at https://flutter-dashboard.appspot.com/#/build. - -Build statuses of commits on this page are sync'ed with most repos in https://github.com/flutter - -### Tree Closures - -The top navigation bar indicates the current tree status. If the tree is closed, -at least one test has failed against the top of tree and flutter/flutter is -currently not accepting new commits. Check on Discord [#tree-status](https://discord.com/channels/608014603317936148/613398423093116959) -for discussion about tree closures. - -Tree closures occur when the latest results for a test have returned as -failing. Logs are found by clicking a task box then clicking "view logs." - - -### Why is a task stuck on "new task" status? - -The dashboard aggregates build results from multiple build environments, -including Cirrus, LUCI, and DeviceLab. Due to limited capacity to run tests on -physical devices, some tests may be batched (run for several commits). - -Flutter infra prioritizes running tasks against the most recent commits. This -leads to some tasks never being run on a commit as the test coverage was -provided from a more recent commit. - -### Why do some tasks have an exclamation point? - -Tasks with exclamation points indicate a task that has been run multiple times. -This indicates extra capacity used for this task. In some cases, this is an -infra issues. If it's common for a task column to be filled with green tasks -with exclamation marks, it indicates that task is flaky. - -### How do I view results for a release branch? - -**TODO(chillers): Update this when release branches are supported by cocoon scheduler** - -Visit https://ci.chromium.org/p/flutter and click the link for the repo and release channel. - -### There's a lot of boxes! Can I filter them? - -Yes, click the settings cog in the top right for various filtering options. - -Some options available include: authors, commit names, and platform run on. -PRs welcome for new filtering options! - -If you're interested in a larger data analysis, the Flutter Infra Team pushes -this data to BigQuery. diff --git a/dashboard/analysis_options.yaml b/dashboard/analysis_options.yaml deleted file mode 100644 index 6919770af..000000000 --- a/dashboard/analysis_options.yaml +++ /dev/null @@ -1,9 +0,0 @@ -include: ../analysis_options.yaml -analyzer: - language: - strict-casts: false - strict-raw-types: true -linter: - rules: - prefer_single_quotes: true - require_trailing_commas: true diff --git a/dashboard/android/.gitignore b/dashboard/android/.gitignore deleted file mode 100644 index 6f568019d..000000000 --- a/dashboard/android/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -gradle-wrapper.jar -/.gradle -/captures/ -/gradlew -/gradlew.bat -/local.properties -GeneratedPluginRegistrant.java - -# Remember to never publicly share your keystore. -# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app -key.properties -**/*.keystore -**/*.jks diff --git a/dashboard/android/app/build.gradle b/dashboard/android/app/build.gradle deleted file mode 100644 index aa7d6aa64..000000000 --- a/dashboard/android/app/build.gradle +++ /dev/null @@ -1,78 +0,0 @@ -plugins { - id "com.android.application" - id "kotlin-android" - id "dev.flutter.flutter-gradle-plugin" -} - -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -def keystoreProperties = new Properties() -def keystorePropertiesFile = rootProject.file('key.properties') -if (keystorePropertiesFile.exists()) { - keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) -} - -android { - namespace "com.appspot.flutter_dashboard.dashboard" - compileSdkVersion flutter.compileSdkVersion - ndkVersion flutter.ndkVersion - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = '1.8' - } - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - - defaultConfig { - applicationId "com.appspot.flutter_dashboard.dashboard" - // You can update the following values to match your application needs. - // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. - minSdkVersion flutter.minSdkVersion - targetSdkVersion flutter.targetSdkVersion - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - } - - signingConfigs { - release { - keyAlias keystoreProperties['keyAlias'] - keyPassword keystoreProperties['keyPassword'] - storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null - storePassword keystoreProperties['storePassword'] - } - } - buildTypes { - release { - signingConfig signingConfigs.release - } - } -} - -flutter { - source '../..' -} - -dependencies {} diff --git a/dashboard/android/app/src/debug/AndroidManifest.xml b/dashboard/android/app/src/debug/AndroidManifest.xml deleted file mode 100644 index 399f6981d..000000000 --- a/dashboard/android/app/src/debug/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/dashboard/android/app/src/main/AndroidManifest.xml b/dashboard/android/app/src/main/AndroidManifest.xml deleted file mode 100644 index c0d048a0e..000000000 --- a/dashboard/android/app/src/main/AndroidManifest.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/dashboard/android/app/src/main/kotlin/com/appspot/flutter_dashboard/dashboard/MainActivity.kt b/dashboard/android/app/src/main/kotlin/com/appspot/flutter_dashboard/dashboard/MainActivity.kt deleted file mode 100644 index a279855f1..000000000 --- a/dashboard/android/app/src/main/kotlin/com/appspot/flutter_dashboard/dashboard/MainActivity.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.appspot.flutter_dashboard.dashboard - -import android.os.Bundle -import android.view.WindowManager - -import io.flutter.embedding.android.FlutterActivity - -class MainActivity: FlutterActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) - } -} diff --git a/dashboard/android/app/src/main/res/drawable-v21/launch_background.xml b/dashboard/android/app/src/main/res/drawable-v21/launch_background.xml deleted file mode 100644 index f74085f3f..000000000 --- a/dashboard/android/app/src/main/res/drawable-v21/launch_background.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - diff --git a/dashboard/android/app/src/main/res/drawable/launch_background.xml b/dashboard/android/app/src/main/res/drawable/launch_background.xml deleted file mode 100644 index 304732f88..000000000 --- a/dashboard/android/app/src/main/res/drawable/launch_background.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - diff --git a/dashboard/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/dashboard/android/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index db77bb4b7b0906d62b1847e87f15cdcacf6a4f29..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 544 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY3?!3`olAj~WQl7;NpOBzNqJ&XDuZK6ep0G} zXKrG8YEWuoN@d~6R2!h8bpbvhu0Wd6uZuB!w&u2PAxD2eNXD>P5D~Wn-+_Wa#27Xc zC?Zj|6r#X(-D3u$NCt}(Ms06KgJ4FxJVv{GM)!I~&n8Bnc94O7-Hd)cjDZswgC;Qs zO=b+9!WcT8F?0rF7!Uys2bs@gozCP?z~o%U|N3vA*22NaGQG zlg@K`O_XuxvZ&Ks^m&R!`&1=spLvfx7oGDKDwpwW`#iqdw@AL`7MR}m`rwr|mZgU`8P7SBkL78fFf!WnuYWm$5Z0 zNXhDbCv&49sM544K|?c)WrFfiZvCi9h0O)B3Pgg&ebxsLQ05GG~ AQ2+n{ diff --git a/dashboard/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/dashboard/android/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 17987b79bb8a35cc66c3c1fd44f5a5526c1b78be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 442 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBc-sk|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5Xx&nMcT!A!W`0S9QKQy;}1Cl^CgaH=;G9cpY;r$Q>i*pfB zP2drbID<_#qf;rPZx^FqH)F_D#*k@@q03KywUtLX8Ua?`H+NMzkczFPK3lFz@i_kW%1NOn0|D2I9n9wzH8m|-tHjsw|9>@K=iMBhxvkv6m8Y-l zytQ?X=U+MF$@3 zt`~i=@j|6y)RWMK--}M|=T`o&^Ni>IoWKHEbBXz7?A@mgWoL>!*SXo`SZH-*HSdS+ yn*9;$7;m`l>wYBC5bq;=U}IMqLzqbYCidGC!)_gkIk_C@Uy!y&wkt5C($~2D>~)O*cj@FGjOCM)M>_ixfudOh)?xMu#Fs z#}Y=@YDTwOM)x{K_j*Q;dPdJ?Mz0n|pLRx{4n|)f>SXlmV)XB04CrSJn#dS5nK2lM zrZ9#~WelCp7&e13Y$jvaEXHskn$2V!!DN-nWS__6T*l;H&Fopn?A6HZ-6WRLFP=R` zqG+CE#d4|IbyAI+rJJ`&x9*T`+a=p|0O(+s{UBcyZdkhj=yS1>AirP+0R;mf2uMgM zC}@~JfByORAh4SyRgi&!(cja>F(l*O+nd+@4m$|6K6KDn_&uvCpV23&>G9HJp{xgg zoq1^2_p9@|WEo z*X_Uko@K)qYYv~>43eQGMdbiGbo>E~Q& zrYBH{QP^@Sti!`2)uG{irBBq@y*$B zi#&(U-*=fp74j)RyIw49+0MRPMRU)+a2r*PJ$L5roHt2$UjExCTZSbq%V!HeS7J$N zdG@vOZB4v_lF7Plrx+hxo7(fCV&}fHq)$ diff --git a/dashboard/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/dashboard/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index d5f1c8d34e7a88e3f88bea192c3a370d44689c3c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1031 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q8Ax83A=Cw=BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrIztFa`(sgt!6~Yi|1%a`XoT0ojZ}lNrNjb9xjc(B0U1_% zz5^97Xt*%oq$rQy4?0GKNfJ44uvxI)gC`h-NZ|&0-7(qS@?b!5r36oQ}zyZrNO3 zMO=Or+<~>+A&uN&E!^Sl+>xE!QC-|oJv`ApDhqC^EWD|@=#J`=d#Xzxs4ah}w&Jnc z$|q_opQ^2TrnVZ0o~wh<3t%W&flvYGe#$xqda2bR_R zvPYgMcHgjZ5nSA^lJr%;<&0do;O^tDDh~=pIxA#coaCY>&N%M2^tq^U%3DB@ynvKo}b?yu-bFc-u0JHzced$sg7S3zqI(2 z#Km{dPr7I=pQ5>FuK#)QwK?Y`E`B?nP+}U)I#c1+FM*1kNvWG|a(TpksZQ3B@sD~b zpQ2)*V*TdwjFOtHvV|;OsiDqHi=6%)o4b!)x$)%9pGTsE z-JL={-Ffv+T87W(Xpooq<`r*VzWQcgBN$$`u}f>-ZQI1BB8ykN*=e4rIsJx9>z}*o zo~|9I;xof diff --git a/dashboard/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/dashboard/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 4d6372eebdb28e45604e46eeda8dd24651419bc0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1443 zcmb`G{WsKk6vsdJTdFg%tJav9_E4vzrOaqkWF|A724Nly!y+?N9`YV6wZ}5(X(D_N(?!*n3`|_r0Hc?=PQw&*vnU?QTFY zB_MsH|!j$PP;I}?dppoE_gA(4uc!jV&0!l7_;&p2^pxNo>PEcNJv za5_RT$o2Mf!<+r?&EbHH6nMoTsDOa;mN(wv8RNsHpG)`^ymG-S5By8=l9iVXzN_eG%Xg2@Xeq76tTZ*dGh~Lo9vl;Zfs+W#BydUw zCkZ$o1LqWQO$FC9aKlLl*7x9^0q%0}$OMlp@Kk_jHXOjofdePND+j!A{q!8~Jn+s3 z?~~w@4?egS02}8NuulUA=L~QQfm;MzCGd)XhiftT;+zFO&JVyp2mBww?;QByS_1w! zrQlx%{^cMj0|Bo1FjwY@Q8?Hx0cIPF*@-ZRFpPc#bBw{5@tD(5%sClzIfl8WU~V#u zm5Q;_F!wa$BSpqhN>W@2De?TKWR*!ujY;Yylk_X5#~V!L*Gw~;$%4Q8~Mad z@`-kG?yb$a9cHIApZDVZ^U6Xkp<*4rU82O7%}0jjHlK{id@?-wpN*fCHXyXh(bLt* zPc}H-x0e4E&nQ>y%B-(EL=9}RyC%MyX=upHuFhAk&MLbsF0LP-q`XnH78@fT+pKPW zu72MW`|?8ht^tz$iC}ZwLp4tB;Q49K!QCF3@!iB1qOI=?w z7In!}F~ij(18UYUjnbmC!qKhPo%24?8U1x{7o(+?^Zu0Hx81|FuS?bJ0jgBhEMzf< zCgUq7r2OCB(`XkKcN-TL>u5y#dD6D!)5W?`O5)V^>jb)P)GBdy%t$uUMpf$SNV31$ zb||OojAbvMP?T@$h_ZiFLFVHDmbyMhJF|-_)HX3%m=CDI+ID$0^C>kzxprBW)hw(v zr!Gmda);ICoQyhV_oP5+C%?jcG8v+D@9f?Dk*!BxY}dazmrT@64UrP3hlslANK)bq z$67n83eh}OeW&SV@HG95P|bjfqJ7gw$e+`Hxo!4cx`jdK1bJ>YDSpGKLPZ^1cv$ek zIB?0S<#tX?SJCLWdMd{-ME?$hc7A$zBOdIJ)4!KcAwb=VMov)nK;9z>x~rfT1>dS+ zZ6#`2v@`jgbqq)P22H)Tx2CpmM^o1$B+xT6`(v%5xJ(?j#>Q$+rx_R|7TzDZe{J6q zG1*EcU%tE?!kO%^M;3aM6JN*LAKUVb^xz8-Pxo#jR5(-KBeLJvA@-gxNHx0M-ZJLl z;#JwQoh~9V?`UVo#}{6ka@II>++D@%KqGpMdlQ}?9E*wFcf5(#XQnP$Dk5~%iX^>f z%$y;?M0BLp{O3a(-4A?ewryHrrD%cx#Q^%KY1H zNre$ve+vceSLZcNY4U(RBX&)oZn*Py()h)XkE?PL$!bNb{N5FVI2Y%LKEm%yvpyTP z(1P?z~7YxD~Rf<(a@_y` diff --git a/dashboard/android/app/src/main/res/values-night/styles.xml b/dashboard/android/app/src/main/res/values-night/styles.xml deleted file mode 100644 index 06952be74..000000000 --- a/dashboard/android/app/src/main/res/values-night/styles.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - diff --git a/dashboard/android/app/src/main/res/values/styles.xml b/dashboard/android/app/src/main/res/values/styles.xml deleted file mode 100644 index cb1ef8805..000000000 --- a/dashboard/android/app/src/main/res/values/styles.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - diff --git a/dashboard/android/app/src/profile/AndroidManifest.xml b/dashboard/android/app/src/profile/AndroidManifest.xml deleted file mode 100644 index 399f6981d..000000000 --- a/dashboard/android/app/src/profile/AndroidManifest.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - diff --git a/dashboard/android/build.gradle b/dashboard/android/build.gradle deleted file mode 100644 index f7eb7f63c..000000000 --- a/dashboard/android/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -buildscript { - ext.kotlin_version = '1.7.10' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.3.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -allprojects { - repositories { - google() - mavenCentral() - } -} - -rootProject.buildDir = '../build' -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(':app') -} - -tasks.register("clean", Delete) { - delete rootProject.buildDir -} diff --git a/dashboard/android/gradle.properties b/dashboard/android/gradle.properties deleted file mode 100644 index 94adc3a3f..000000000 --- a/dashboard/android/gradle.properties +++ /dev/null @@ -1,3 +0,0 @@ -org.gradle.jvmargs=-Xmx1536M -android.useAndroidX=true -android.enableJetifier=true diff --git a/dashboard/android/gradle/wrapper/gradle-wrapper.properties b/dashboard/android/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 3c472b99c..000000000 --- a/dashboard/android/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,5 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip diff --git a/dashboard/android/settings.gradle b/dashboard/android/settings.gradle deleted file mode 100644 index 55c4ca8b1..000000000 --- a/dashboard/android/settings.gradle +++ /dev/null @@ -1,20 +0,0 @@ -pluginManagement { - def flutterSdkPath = { - def properties = new Properties() - file("local.properties").withInputStream { properties.load(it) } - def flutterSdkPath = properties.getProperty("flutter.sdk") - assert flutterSdkPath != null, "flutter.sdk not set in local.properties" - return flutterSdkPath - } - settings.ext.flutterSdkPath = flutterSdkPath() - - includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") - - plugins { - id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false - } -} - -include ":app" - -apply from: "${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/dashboard/assets/apple.png b/dashboard/assets/apple.png deleted file mode 100644 index 21fcb568e8fb0ab219eb5ab95ae775b6532643ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2411 zcmeHI={pn(A03Qg#xf&@3^HS9F3DEJMD}f(MMAokGS+NkEzN7}OVZ0=jFEAzV=22N z$_gSW6)Y0s;U4gsiR12&Y>05BPab z8B(cG2LNz4;OrdCPwl_=?*jiX3!I#s3>1qZPvfqH5wJu67dH=(7sSU8h6o4>2}5Bb zXGF!|;u2>i5mM4J=VXy6Ie7&|C1n*g^e^ff=QS^AY3u4;(l;HVXx|Lwc?10M#5hDS!n#wRAHra#Wk%`Yr2eOg{wTi@9H{O6ag?H$hU-e3EFe?2(- zcJ%$n&tut_W2Do*T%NX;W~)wK0KcmHwawCs9GD$&RhjJL)ol*G2+uKxdAQRLlEd~^Om?N{ zJ0D^W=0}&JaTQn@sGQ|5KUKd&nj~5|a`XJ`+qI+V&Sl`HaX+G>tOUfK zw_JP|ybai3-x9BG6EIS|;vViigy(=Tca3z!I*ULftfF=7uocut`IABh4Z|~cNz2y< z+SHiTJ5LSl9i?kaGps9F_02SqUQ ziby?q(T3JXeAa!qEqy|`WVkWW8no~T6_wUuo;mEhDJdx_(AzPM0Xq8sn3yfphaTdh zzcN%iq&YSvrEU2=F|*A=*`L1DSs5SntUKQt{<>R>OP|VD_Nnw4?ZX|qEsvr1k^X*S zhBiM8lXX?*FbV()yuIq7Zm?{jG?-`WO8aY;VAIs1JbrOI%xE8V= z|EBF`7K+5>g$Bpd-Bdmrq|{5`TZ&<#w$n@xn08u&OajZ9wk5#tVmUpZ#FD3}pNrEV zwt!&!u~|8$xU*jGl39+lEj3_sNq{%1Qr_!b91Hk10u=Xx*isFn8BarymD*lpePlUL zSvr-TOACZ`8`^FXkiCxBX-X{1^=+r&JLn!8>aImp4+%J8Xa z6wO-n`11uG()4jTLh;$}H9Kr86DXZ#cAZ(`fwfY-&Sv_>t6pYfNeb@|A467!6Tt&$ z_Q>GfrH-EtRRJNyD1RL|+8!Do8Mnut8ZeIXm4x2}2K5l@g*h&IIR=m&`*K1*=hwbl zY7S>WqHFl#X3diP?9cmgstbgCQ%&XatSZ4I>-I{kM>0x1?d)DBdJDsb+4`nwP0_(a z4EVl+TR8v*qvY)=n)F%H0xxz?HL5uK>HvETR3`)Y3$C#>l8l`?MrnQHLKv%H{F5?H z{}~&yz8D5is+%>T(Z34bse9Fl{Oca&IOFz5l8}$!v*{VZqSla|oj<;k>^5Qz!jGRF Zb1@>I_}JNkhEj;^Ldh0cW5yO*ELlU0 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 vQX%ZG@s}hYk%rNkOjEL8T diff --git a/dashboard/assets/googleLogo.png b/dashboard/assets/googleLogo.png deleted file mode 100644 index 437027eab8be37550fcf6068d693cf13b45d62dc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1307 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 ROrih)002ovPDHLkV1o5~qfY<; diff --git a/dashboard/ios/.gitignore b/dashboard/ios/.gitignore deleted file mode 100644 index e96ef602b..000000000 --- a/dashboard/ios/.gitignore +++ /dev/null @@ -1,32 +0,0 @@ -*.mode1v3 -*.mode2v3 -*.moved-aside -*.pbxuser -*.perspectivev3 -**/*sync/ -.sconsign.dblite -.tags* -**/.vagrant/ -**/DerivedData/ -Icon? -**/Pods/ -**/.symlinks/ -profile -xcuserdata -**/.generated/ -Flutter/App.framework -Flutter/Flutter.framework -Flutter/Flutter.podspec -Flutter/Generated.xcconfig -Flutter/app.flx -Flutter/app.zip -Flutter/flutter_assets/ -Flutter/flutter_export_environment.sh -ServiceDefinitions.json -Runner/GeneratedPluginRegistrant.* - -# Exceptions to above rules. -!default.mode1v3 -!default.mode2v3 -!default.pbxuser -!default.perspectivev3 diff --git a/dashboard/ios/Flutter/AppFrameworkInfo.plist b/dashboard/ios/Flutter/AppFrameworkInfo.plist deleted file mode 100644 index 9367d483e..000000000 --- a/dashboard/ios/Flutter/AppFrameworkInfo.plist +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - App - CFBundleIdentifier - io.flutter.flutter.app - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - App - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1.0 - MinimumOSVersion - 8.0 - - diff --git a/dashboard/ios/Flutter/Debug.xcconfig b/dashboard/ios/Flutter/Debug.xcconfig deleted file mode 100644 index ec97fc6f3..000000000 --- a/dashboard/ios/Flutter/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" -#include "Generated.xcconfig" diff --git a/dashboard/ios/Flutter/Release.xcconfig b/dashboard/ios/Flutter/Release.xcconfig deleted file mode 100644 index c4855bfe2..000000000 --- a/dashboard/ios/Flutter/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" -#include "Generated.xcconfig" diff --git a/dashboard/ios/Podfile b/dashboard/ios/Podfile deleted file mode 100644 index 1e8c3c90a..000000000 --- a/dashboard/ios/Podfile +++ /dev/null @@ -1,41 +0,0 @@ -# Uncomment this line to define a global platform for your project -# platform :ios, '9.0' - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug' => :debug, - 'Profile' => :release, - 'Release' => :release, -} - -def flutter_root - generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) - unless File.exist?(generated_xcode_build_settings_path) - raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" - end - - File.foreach(generated_xcode_build_settings_path) do |line| - matches = line.match(/FLUTTER_ROOT\=(.*)/) - return matches[1].strip if matches - end - raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" -end - -require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) - -flutter_ios_podfile_setup - -target 'Runner' do - use_frameworks! - use_modular_headers! - - flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) -end - -post_install do |installer| - installer.pods_project.targets.each do |target| - flutter_additional_ios_build_settings(target) - end -end diff --git a/dashboard/ios/Runner.xcodeproj/project.pbxproj b/dashboard/ios/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index 524dbadaf..000000000 --- a/dashboard/ios/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,471 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 46; - objects = { - -/* Begin PBXBuildFile section */ - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; -/* End PBXBuildFile section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 9705A1C41CF9048500538489 /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; - 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 97C146EB1CF9000F007C117D /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 9740EEB11CF90186004384FC /* Flutter */ = { - isa = PBXGroup; - children = ( - 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 9740EEB31CF90195004384FC /* Generated.xcconfig */, - ); - name = Flutter; - sourceTree = ""; - }; - 97C146E51CF9000F007C117D = { - isa = PBXGroup; - children = ( - 9740EEB11CF90186004384FC /* Flutter */, - 97C146F01CF9000F007C117D /* Runner */, - 97C146EF1CF9000F007C117D /* Products */, - ); - sourceTree = ""; - }; - 97C146EF1CF9000F007C117D /* Products */ = { - isa = PBXGroup; - children = ( - 97C146EE1CF9000F007C117D /* Runner.app */, - ); - name = Products; - sourceTree = ""; - }; - 97C146F01CF9000F007C117D /* Runner */ = { - isa = PBXGroup; - children = ( - 97C146FA1CF9000F007C117D /* Main.storyboard */, - 97C146FD1CF9000F007C117D /* Assets.xcassets */, - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, - 97C147021CF9000F007C117D /* Info.plist */, - 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, - 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, - 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, - 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, - ); - path = Runner; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 97C146ED1CF9000F007C117D /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - 9740EEB61CF901F6004384FC /* Run Script */, - 97C146EA1CF9000F007C117D /* Sources */, - 97C146EB1CF9000F007C117D /* Frameworks */, - 97C146EC1CF9000F007C117D /* Resources */, - 9705A1C41CF9048500538489 /* Embed Frameworks */, - 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = Runner; - productName = Runner; - productReference = 97C146EE1CF9000F007C117D /* Runner.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 97C146E61CF9000F007C117D /* Project object */ = { - isa = PBXProject; - attributes = { - LastUpgradeCheck = 1020; - ORGANIZATIONNAME = ""; - TargetAttributes = { - 97C146ED1CF9000F007C117D = { - CreatedOnToolsVersion = 7.3.1; - LastSwiftMigration = 1100; - }; - }; - }; - buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 97C146E51CF9000F007C117D; - productRefGroup = 97C146EF1CF9000F007C117D /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 97C146ED1CF9000F007C117D /* Runner */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 97C146EC1CF9000F007C117D /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, - 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, - 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, - 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Thin Binary"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; - }; - 9740EEB61CF901F6004384FC /* Run Script */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputPaths = ( - ); - name = "Run Script"; - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 97C146EA1CF9000F007C117D /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, - 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - 97C146FA1CF9000F007C117D /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C146FB1CF9000F007C117D /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - 97C147001CF9000F007C117D /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 249021D3217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Profile; - }; - 249021D4217E4FDB00AE95B9 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.example.appFlutter; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Profile; - }; - 97C147031CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 97C147041CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu99; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 9.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SUPPORTED_PLATFORMS = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - 97C147061CF9000F007C117D /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.example.appFlutter; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Debug; - }; - 97C147071CF9000F007C117D /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; - ENABLE_BITCODE = NO; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.example.appFlutter; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - VERSIONING_SYSTEM = "apple-generic"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147031CF9000F007C117D /* Debug */, - 97C147041CF9000F007C117D /* Release */, - 249021D3217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 97C147061CF9000F007C117D /* Debug */, - 97C147071CF9000F007C117D /* Release */, - 249021D4217E4FDB00AE95B9 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 97C146E61CF9000F007C117D /* Project object */; -} diff --git a/dashboard/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/dashboard/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a62..000000000 --- a/dashboard/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/dashboard/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/dashboard/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003..000000000 --- a/dashboard/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/dashboard/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/dashboard/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c5e..000000000 --- a/dashboard/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/dashboard/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/dashboard/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index a28140cfd..000000000 --- a/dashboard/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,91 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/dashboard/ios/Runner.xcworkspace/contents.xcworkspacedata b/dashboard/ios/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 1d526a16e..000000000 --- a/dashboard/ios/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/dashboard/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/dashboard/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003..000000000 --- a/dashboard/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/dashboard/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/dashboard/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings deleted file mode 100644 index f9b0d7c5e..000000000 --- a/dashboard/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings +++ /dev/null @@ -1,8 +0,0 @@ - - - - - PreviewsEnabled - - - diff --git a/dashboard/ios/Runner/AppDelegate.swift b/dashboard/ios/Runner/AppDelegate.swift deleted file mode 100644 index 70693e4a8..000000000 --- a/dashboard/ios/Runner/AppDelegate.swift +++ /dev/null @@ -1,13 +0,0 @@ -import UIKit -import Flutter - -@UIApplicationMain -@objc class AppDelegate: FlutterAppDelegate { - override func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? - ) -> Bool { - GeneratedPluginRegistrant.register(with: self) - return super.application(application, didFinishLaunchingWithOptions: launchOptions) - } -} diff --git a/dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index d36b1fab2..000000000 --- a/dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "images" : [ - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "20x20", - "idiom" : "iphone", - "filename" : "Icon-App-20x20@3x.png", - "scale" : "3x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "iphone", - "filename" : "Icon-App-29x29@3x.png", - "scale" : "3x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "iphone", - "filename" : "Icon-App-40x40@3x.png", - "scale" : "3x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@2x.png", - "scale" : "2x" - }, - { - "size" : "60x60", - "idiom" : "iphone", - "filename" : "Icon-App-60x60@3x.png", - "scale" : "3x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@1x.png", - "scale" : "1x" - }, - { - "size" : "20x20", - "idiom" : "ipad", - "filename" : "Icon-App-20x20@2x.png", - "scale" : "2x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@1x.png", - "scale" : "1x" - }, - { - "size" : "29x29", - "idiom" : "ipad", - "filename" : "Icon-App-29x29@2x.png", - "scale" : "2x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@1x.png", - "scale" : "1x" - }, - { - "size" : "40x40", - "idiom" : "ipad", - "filename" : "Icon-App-40x40@2x.png", - "scale" : "2x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@1x.png", - "scale" : "1x" - }, - { - "size" : "76x76", - "idiom" : "ipad", - "filename" : "Icon-App-76x76@2x.png", - "scale" : "2x" - }, - { - "size" : "83.5x83.5", - "idiom" : "ipad", - "filename" : "Icon-App-83.5x83.5@2x.png", - "scale" : "2x" - }, - { - "size" : "1024x1024", - "idiom" : "ios-marketing", - "filename" : "Icon-App-1024x1024@1x.png", - "scale" : "1x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png deleted file mode 100644 index dc9ada4725e9b0ddb1deab583e5b5102493aa332..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10932 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 diff --git a/dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png deleted file mode 100644 index f091b6b0bca859a3f474b03065bef75ba58a9e4c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1588 zcmV-42Fv-0P)C1SqPt}wig>|5Crh^=oyX$BK<}M8eLU3e2hGT;=G|!_SP)7zNI6fqUMB=)y zRAZ>eDe#*r`yDAVgB_R*LB*MAc)8(b{g{9McCXW!lq7r(btRoB9!8B-#AI6JMb~YFBEvdsV)`mEQO^&#eRKx@b&x- z5lZm*!WfD8oCLzfHGz#u7sT0^VLMI1MqGxF^v+`4YYnVYgk*=kU?HsSz{v({E3lb9 z>+xILjBN)t6`=g~IBOelGQ(O990@BfXf(DRI5I$qN$0Gkz-FSc$3a+2fX$AedL4u{ z4V+5Ong(9LiGcIKW?_352sR;LtDPmPJXI{YtT=O8=76o9;*n%_m|xo!i>7$IrZ-{l z-x3`7M}qzHsPV@$v#>H-TpjDh2UE$9g6sysUREDy_R(a)>=eHw-WAyfIN z*qb!_hW>G)Tu8nSw9yn#3wFMiLcfc4pY0ek1}8(NqkBR@t4{~oC>ryc-h_ByH(Cg5 z>ao-}771+xE3um9lWAY1FeQFxowa1(!J(;Jg*wrg!=6FdRX+t_<%z&d&?|Bn){>zm zZQj(aA_HeBY&OC^jj*)N`8fa^ePOU72VpInJoI1?`ty#lvlNzs(&MZX+R%2xS~5Kh zX*|AU4QE#~SgPzOXe9>tRj>hjU@c1k5Y_mW*Jp3fI;)1&g3j|zDgC+}2Q_v%YfDax z!?umcN^n}KYQ|a$Lr+51Nf9dkkYFSjZZjkma$0KOj+;aQ&721~t7QUKx61J3(P4P1 zstI~7-wOACnWP4=8oGOwz%vNDqD8w&Q`qcNGGrbbf&0s9L0De{4{mRS?o0MU+nR_! zrvshUau0G^DeMhM_v{5BuLjb#Hh@r23lDAk8oF(C+P0rsBpv85EP>4CVMx#04MOfG z;P%vktHcXwTj~+IE(~px)3*MY77e}p#|c>TD?sMatC0Tu4iKKJ0(X8jxQY*gYtxsC z(zYC$g|@+I+kY;dg_dE>scBf&bP1Nc@Hz<3R)V`=AGkc;8CXqdi=B4l2k|g;2%#m& z*jfX^%b!A8#bI!j9-0Fi0bOXl(-c^AB9|nQaE`*)Hw+o&jS9@7&Gov#HbD~#d{twV zXd^Tr^mWLfFh$@Dr$e;PBEz4(-2q1FF0}c;~B5sA}+Q>TOoP+t>wf)V9Iy=5ruQa;z)y zI9C9*oUga6=hxw6QasLPnee@3^Rr*M{CdaL5=R41nLs(AHk_=Y+A9$2&H(B7!_pURs&8aNw7?`&Z&xY_Ye z)~D5Bog^td-^QbUtkTirdyK^mTHAOuptDflut!#^lnKqU md>ggs(5nOWAqO?umG&QVYK#ibz}*4>0000U6E9hRK9^#O7(mu>ETqrXGsduA8$)?`v2seloOCza43C{NQ$$gAOH**MCn0Q?+L7dl7qnbRdqZ8LSVp1ItDxhxD?t@5_yHg6A8yI zC*%Wgg22K|8E#!~cTNYR~@Y9KepMPrrB8cABapAFa=`H+UGhkXUZV1GnwR1*lPyZ;*K(i~2gp|@bzp8}og7e*#% zEnr|^CWdVV!-4*Y_7rFvlww2Ze+>j*!Z!pQ?2l->4q#nqRu9`ELo6RMS5=br47g_X zRw}P9a7RRYQ%2Vsd0Me{_(EggTnuN6j=-?uFS6j^u69elMypu?t>op*wBx<=Wx8?( ztpe^(fwM6jJX7M-l*k3kEpWOl_Vk3@(_w4oc}4YF4|Rt=2V^XU?#Yz`8(e?aZ@#li0n*=g^qOcVpd-Wbok=@b#Yw zqn8u9a)z>l(1kEaPYZ6hwubN6i<8QHgsu0oE) ziJ(p;Wxm>sf!K+cw>R-(^Y2_bahB+&KI9y^);#0qt}t-$C|Bo71lHi{_+lg#f%RFy z0um=e3$K3i6K{U_4K!EX?F&rExl^W|G8Z8;`5z-k}OGNZ0#WVb$WCpQu-_YsiqKP?BB# vzVHS-CTUF4Ozn5G+mq_~Qqto~ahA+K`|lyv3(-e}00000NkvXXu0mjfd`9t{ diff --git a/dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png deleted file mode 100644 index d0ef06e7edb86cdfe0d15b4b0d98334a86163658..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1716 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 diff --git a/dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png deleted file mode 100644 index c8f9ed8f5cee1c98386d13b17e89f719e83555b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1895 zcmV-t2blPYP)FQtfgmafE#=YDCq`qUBt#QpG%*H6QHY765~R=q zZ6iudfM}q!Pz#~9JgOi8QJ|DSu?1-*(kSi1K4#~5?#|rh?sS)(-JQqX*}ciXJ56_H zdw=^s_srbAdqxlvGyrgGet#6T7_|j;95sL%MtM;q86vOxKM$f#puR)Bjv9Zvz9-di zXOTSsZkM83)E9PYBXC<$6(|>lNLVBb&&6y{NByFCp%6+^ALR@NCTse_wqvNmSWI-m z!$%KlHFH2omF!>#%1l3LTZg(s7eof$7*xB)ZQ0h?ejh?Ta9fDv59+u#MokW+1t8Zb zgHv%K(u9G^Lv`lh#f3<6!JVTL3(dCpxHbnbA;kKqQyd1~^Xe0VIaYBSWm6nsr;dFj z4;G-RyL?cYgsN1{L4ZFFNa;8)Rv0fM0C(~Tkit94 zz#~A)59?QjD&pAPSEQ)p8gP|DS{ng)j=2ux)_EzzJ773GmQ_Cic%3JJhC0t2cx>|v zJcVusIB!%F90{+}8hG3QU4KNeKmK%T>mN57NnCZ^56=0?&3@!j>a>B43pi{!u z7JyDj7`6d)qVp^R=%j>UIY6f+3`+qzIc!Y_=+uN^3BYV|o+$vGo-j-Wm<10%A=(Yk^beI{t%ld@yhKjq0iNjqN4XMGgQtbKubPM$JWBz}YA65k%dm*awtC^+f;a-x4+ddbH^7iDWGg&N0n#MW{kA|=8iMUiFYvMoDY@sPC#t$55gn6ykUTPAr`a@!(;np824>2xJthS z*ZdmT`g5-`BuJs`0LVhz+D9NNa3<=6m;cQLaF?tCv8)zcRSh66*Z|vXhG@$I%U~2l z?`Q zykI#*+rQ=z6Jm=Bui-SfpDYLA=|vzGE(dYm=OC8XM&MDo7ux4UF1~0J1+i%aCUpRe zt3L_uNyQ*cE(38Uy03H%I*)*Bh=Lb^Xj3?I^Hnbeq72(EOK^Y93CNp*uAA{5Lc=ky zx=~RKa4{iTm{_>_vSCm?$Ej=i6@=m%@VvAITnigVg{&@!7CDgs908761meDK5azA} z4?=NOH|PdvabgJ&fW2{Mo$Q0CcD8Qc84%{JPYt5EiG{MdLIAeX%T=D7NIP4%Hw}p9 zg)==!2Lbp#j{u_}hMiao9=!VSyx0gHbeCS`;q&vzeq|fs`y&^X-lso(Ls@-706qmA z7u*T5PMo_w3{se1t2`zWeO^hOvTsohG_;>J0wVqVe+n)AbQCx)yh9;w+J6?NF5Lmo zecS@ieAKL8%bVd@+-KT{yI|S}O>pYckUFs;ry9Ow$CD@ztz5K-*D$^{i(_1llhSh^ zEkL$}tsQt5>QA^;QgjgIfBDmcOgi5YDyu?t6vSnbp=1+@6D& z5MJ}B8q;bRlVoxasyhcUF1+)o`&3r0colr}QJ3hcSdLu;9;td>kf@Tcn<@9sIx&=m z;AD;SCh95=&p;$r{Xz3iWCO^MX83AGJ(yH&eTXgv|0=34#-&WAmw{)U7OU9!Wz^!7 zZ%jZFi@JR;>Mhi7S>V7wQ176|FdW2m?&`qa(ScO^CFPR80HucLHOTy%5s*HR0^8)i h0WYBP*#0Ks^FNSabJA*5${_#%002ovPDHLkV1oKhTl@e3 diff --git a/dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png deleted file mode 100644 index a6d6b8609df07bf62e5100a53a01510388bd2b22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ diff --git a/dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png deleted file mode 100644 index a6d6b8609df07bf62e5100a53a01510388bd2b22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2665 zcmV-v3YPVWP)oFh3q0MFesq&64WThn3$;G69TfjsAv=f2G9}p zgSx99+!YV6qME!>9MD13x)k(+XE7W?_O4LoLb5ND8 zaV{9+P@>42xDfRiYBMSgD$0!vssptcb;&?u9u(LLBKmkZ>RMD=kvD3h`sk6!QYtBa ztlZI#nu$8lJ^q2Z79UTgZe>BU73(Aospiq+?SdMt8lDZ;*?@tyWVZVS_Q7S&*tJaiRlJ z+aSMOmbg3@h5}v;A*c8SbqM3icg-`Cnwl;7Ts%A1RkNIp+Txl-Ckkvg4oxrqGA5ewEgYqwtECD<_3Egu)xGllKt&J8g&+=ac@Jq4-?w6M3b*>w5 z69N3O%=I^6&UL5gZ!}trC7bUj*12xLdkNs~Bz4QdJJ*UDZox2UGR}SNg@lmOvhCc~ z*f_UeXv(=#I#*7>VZx2ObEN~UoGUTl=-@)E;YtCRZ>SVp$p9yG5hEFZ!`wI!spd)n zSk+vK0Vin7FL{7f&6OB%f;SH22dtbcF<|9fi2Fp%q4kxL!b1#l^)8dUwJ zwEf{(wJj@8iYDVnKB`eSU+;ml-t2`@%_)0jDM`+a46xhDbBj2+&Ih>1A>6aky#(-SYyE{R3f#y57wfLs z6w1p~$bp;6!9DX$M+J~S@D6vJAaElETnsX4h9a5tvPhC3L@qB~bOzkL@^z0k_hS{T4PF*TDrgdXp+dzsE? z>V|VR035Pl9n5&-RePFdS{7KAr2vPOqR9=M$vXA1Yy5>w;EsF`;OK{2pkn-kpp9Pw z)r;5JfJKKaT$4qCb{TaXHjb$QA{y0EYy*+b1XI;6Ah- zw13P)xT`>~eFoJC!>{2XL(a_#upp3gaR1#5+L(Jmzp4TBnx{~WHedpJ1ch8JFk~Sw z>F+gN+i+VD?gMXwcIhn8rz`>e>J^TI3E-MW>f}6R-pL}>WMOa0k#jN+`RyUVUC;#D zg|~oS^$6%wpF{^Qr+}X>0PKcr3Fc&>Z>uv@C);pwDs@2bZWhYP!rvGx?_|q{d`t<*XEb#=aOb=N+L@CVBGqImZf&+a zCQEa3$~@#kC);pasdG=f6tuIi0PO-y&tvX%>Mv=oY3U$nD zJ#gMegnQ46pq+3r=;zmgcG+zRc9D~c>z+jo9&D+`E6$LmyFqlmCYw;-Zooma{sR@~ z)_^|YL1&&@|GXo*pivH7k!msl+$Sew3%XJnxajt0K%3M6Bd&YFNy9}tWG^aovK2eX z1aL1%7;KRDrA@eG-Wr6w+;*H_VD~qLiVI`{_;>o)k`{8xa3EJT1O_>#iy_?va0eR? zDV=N%;Zjb%Z2s$@O>w@iqt!I}tLjGk!=p`D23I}N4Be@$(|iSA zf3Ih7b<{zqpDB4WF_5X1(peKe+rASze%u8eKLn#KKXt;UZ+Adf$_TO+vTqshLLJ5c z52HucO=lrNVae5XWOLm!V@n-ObU11!b+DN<$RuU+YsrBq*lYT;?AwJpmNKniF0Q1< zJCo>Q$=v$@&y=sj6{r!Y&y&`0$-I}S!H_~pI&2H8Z1C|BX4VgZ^-! zje3-;x0PBD!M`v*J_)rL^+$<1VJhH*2Fi~aA7s&@_rUHYJ9zD=M%4AFQ`}k8OC$9s XsPq=LnkwKG00000NkvXXu0mjfhAk5^ diff --git a/dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png deleted file mode 100644 index 75b2d164a5a98e212cca15ea7bf2ab5de5108680..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3831 zcmVjJBgitF5mAp-i>4+KS_oR{|13AP->1TD4=w)g|)JHOx|a2Wk1Va z!k)vP$UcQ#mdj%wNQoaJ!w>jv_6&JPyutpQps?s5dmDQ>`%?Bvj>o<%kYG!YW6H-z zu`g$@mp`;qDR!51QaS}|ZToSuAGcJ7$2HF0z`ln4t!#Yg46>;vGG9N9{V@9z#}6v* zfP?}r6b{*-C*)(S>NECI_E~{QYzN5SXRmVnP<=gzP+_Sp(Aza_hKlZ{C1D&l*(7IKXxQC1Z9#6wx}YrGcn~g%;icdw>T0Rf^w0{ z$_wn1J+C0@!jCV<%Go5LA45e{5gY9PvZp8uM$=1}XDI+9m7!A95L>q>>oe0$nC->i zeexUIvq%Uk<-$>DiDb?!In)lAmtuMWxvWlk`2>4lNuhSsjAf2*2tjT`y;@d}($o)S zn(+W&hJ1p0xy@oxP%AM15->wPLp{H!k)BdBD$toBpJh+crWdsNV)qsHaqLg2_s|Ih z`8E9z{E3sA!}5aKu?T!#enD(wLw?IT?k-yWVHZ8Akz4k5(TZJN^zZgm&zM28sfTD2BYJ|Fde3Xzh;;S` z=GXTnY4Xc)8nYoz6&vF;P7{xRF-{|2Xs5>a5)@BrnQ}I(_x7Cgpx#5&Td^4Q9_FnQ zX5so*;#8-J8#c$OlA&JyPp$LKUhC~-e~Ij!L%uSMu!-VZG7Hx-L{m2DVR2i=GR(_% zCVD!4N`I)&Q5S`?P&fQZ=4#Dgt_v2-DzkT}K(9gF0L(owe-Id$Rc2qZVLqI_M_DyO z9@LC#U28_LU{;wGZ&))}0R2P4MhajKCd^K#D+JJ&JIXZ_p#@+7J9A&P<0kdRujtQ_ zOy>3=C$kgi6$0pW06KaLz!21oOryKM3ZUOWqppndxfH}QpgjEJ`j7Tzn5bk6K&@RA?vl##y z$?V~1E(!wB5rH`>3nc&@)|#<1dN2cMzzm=PGhQ|Yppne(C-Vlt450IXc`J4R0W@I7 zd1e5uW6juvO%ni(WX7BsKx3MLngO7rHO;^R5I~0^nE^9^E_eYLgiR9&KnJ)pBbfno zSVnW$0R+&6jOOsZ82}nJ126+c|%svPo;TeUku<2G7%?$oft zyaO;tVo}(W)VsTUhq^XmFi#2z%-W9a{7mXn{uzivYQ_d6b7VJG{77naW(vHt-uhnY zVN#d!JTqVh(7r-lhtXVU6o})aZbDt_;&wJVGl2FKYFBFpU-#9U)z#(A%=IVnqytR$SY-sO( z($oNE09{D^@OuYPz&w~?9>Fl5`g9u&ecFGhqX=^#fmR=we0CJw+5xna*@oHnkahk+ z9aWeE3v|An+O5%?4fA&$Fgu~H_YmqR!yIU!bFCk4!#pAj%(lI(A5n)n@Id#M)O9Yx zJU9oKy{sRAIV3=5>(s8n{8ryJ!;ho}%pn6hZKTKbqk=&m=f*UnK$zW3YQP*)pw$O* zIfLA^!-bmBl6%d_n$#tP8Zd_(XdA*z*WH|E_yILwjtI~;jK#v-6jMl^?<%Y%`gvpwv&cFb$||^v4D&V=aNy?NGo620jL3VZnA%s zH~I|qPzB~e(;p;b^gJr7Ure#7?8%F0m4vzzPy^^(q4q1OdthF}Fi*RmVZN1OwTsAP zn9CZP`FazX3^kG(KodIZ=Kty8DLTy--UKfa1$6XugS zk%6v$Kmxt6U!YMx0JQ)0qX*{CXwZZk$vEROidEc7=J-1;peNat!vS<3P-FT5po>iE z!l3R+<`#x|+_hw!HjQGV=8!q|76y8L7N8gP3$%0kfush|u0uU^?dKBaeRSBUpOZ0c z62;D&Mdn2}N}xHRFTRI?zRv=>=AjHgH}`2k4WK=#AHB)UFrR-J87GgX*x5fL^W2#d z=(%K8-oZfMO=i{aWRDg=FX}UubM4eotRDcn;OR#{3q=*?3mE3_oJ-~prjhxh%PgQT zyn)Qozaq0@o&|LEgS{Ind4Swsr;b`u185hZPOBLL<`d2%^Yp1?oL)=jnLi;Zo0ZDliTtQ^b5SmfIMe{T==zZkbvn$KTQGlbG8w}s@M3TZnde;1Am46P3juKb zl9GU&3F=q`>j!`?SyH#r@O59%@aMX^rx}Nxe<>NqpUp5=lX1ojGDIR*-D^SDuvCKF z?3$xG(gVUsBERef_YjPFl^rU9EtD{pt z0CXwpN7BN3!8>hajGaTVk-wl=9rxmfWtIhC{mheHgStLi^+Nz12a?4r(fz)?3A%at zMlvQmL<2-R)-@G1wJ0^zQK%mR=r4d{Y3fHp){nWXUL#|CqXl(+v+qDh>FkF9`eWrW zfr^D%LNfOcTNvtx0JXR35J0~Jpi2#P3Q&80w+nqNfc}&G0A~*)lGHKv=^FE+b(37|)zL;KLF>oiGfb(?&1 zV3XRu!Sw>@quKiab%g6jun#oZ%!>V#A%+lNc?q>6+VvyAn=kf_6z^(TZUa4Eelh{{ zqFX-#dY(EV@7l$NE&kv9u9BR8&Ojd#ZGJ6l8_BW}^r?DIS_rU2(XaGOK z225E@kH5Opf+CgD^{y29jD4gHbGf{1MD6ggQ&%>UG4WyPh5q_tb`{@_34B?xfSO*| zZv8!)q;^o-bz`MuxXk*G^}(6)ACb@=Lfs`Hxoh>`Y0NE8QRQ!*p|SH@{r8=%RKd4p z+#Ty^-0kb=-H-O`nAA3_6>2z(D=~Tbs(n8LHxD0`R0_ATFqp-SdY3(bZ3;VUM?J=O zKCNsxsgt@|&nKMC=*+ZqmLHhX1KHbAJs{nGVMs6~TiF%Q)P@>!koa$%oS zjXa=!5>P`vC-a}ln!uH1ooeI&v?=?v7?1n~P(wZ~0>xWxd_Aw;+}9#eULM7M8&E?Y zC-ZLhi3RoM92SXUb-5i-Lmt5_rfjE{6y^+24`y$1lywLyHO!)Boa7438K4#iLe?rh z2O~YGSgFUBH?og*6=r9rme=peP~ah`(8Zt7V)j5!V0KPFf_mebo3z95U8(up$-+EA^9dTRLq>Yl)YMBuch9%=e5B`Vnb>o zt03=kq;k2TgGe4|lGne&zJa~h(UGutjP_zr?a7~#b)@15XNA>Dj(m=gg2Q5V4-$)D|Q9}R#002ovPDHLkV1o7DH3k3x diff --git a/dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/dashboard/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png deleted file mode 100644 index c4df70d39da7941ef3f6dcb7f06a192d8dcb308d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1888 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 iW8Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v diff --git a/dashboard/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/dashboard/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png deleted file mode 100644 index 9da19eacad3b03bb08bbddbbf4ac48dd78b3d838..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v diff --git a/dashboard/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/dashboard/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png deleted file mode 100644 index 9da19eacad3b03bb08bbddbbf4ac48dd78b3d838..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68 zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J Q1PU{Fy85}Sb4q9e0B4a5jsO4v diff --git a/dashboard/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/dashboard/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md deleted file mode 100644 index 89c2725b7..000000000 --- a/dashboard/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Launch Screen Assets - -You can customize the launch screen with your own desired assets by replacing the image files in this directory. - -You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/dashboard/ios/Runner/Base.lproj/LaunchScreen.storyboard b/dashboard/ios/Runner/Base.lproj/LaunchScreen.storyboard deleted file mode 100644 index f2e259c7c..000000000 --- a/dashboard/ios/Runner/Base.lproj/LaunchScreen.storyboard +++ /dev/null @@ -1,37 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/dashboard/ios/Runner/Base.lproj/Main.storyboard b/dashboard/ios/Runner/Base.lproj/Main.storyboard deleted file mode 100644 index f3c28516f..000000000 --- a/dashboard/ios/Runner/Base.lproj/Main.storyboard +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/dashboard/ios/Runner/Info.plist b/dashboard/ios/Runner/Info.plist deleted file mode 100644 index ef0ebb995..000000000 --- a/dashboard/ios/Runner/Info.plist +++ /dev/null @@ -1,45 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - app_flutter - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleSignature - ???? - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSRequiresIPhoneOS - - UILaunchStoryboardName - LaunchScreen - UIMainStoryboardFile - Main - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UISupportedInterfaceOrientations~ipad - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UIViewControllerBasedStatusBarAppearance - - - diff --git a/dashboard/ios/Runner/Runner-Bridging-Header.h b/dashboard/ios/Runner/Runner-Bridging-Header.h deleted file mode 100644 index 308a2a560..000000000 --- a/dashboard/ios/Runner/Runner-Bridging-Header.h +++ /dev/null @@ -1 +0,0 @@ -#import "GeneratedPluginRegistrant.h" diff --git a/dashboard/lib/build_dashboard_page.dart b/dashboard/lib/build_dashboard_page.dart deleted file mode 100644 index c1873ce6b..000000000 --- a/dashboard/lib/build_dashboard_page.dart +++ /dev/null @@ -1,467 +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:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_dashboard/model/branch.pb.dart'; -import 'package:provider/provider.dart'; -import 'package:truncate/truncate.dart'; -import 'package:url_launcher/url_launcher.dart'; - -import 'dashboard_navigation_drawer.dart'; -import 'logic/task_grid_filter.dart'; -import 'service/cocoon.dart'; -import 'state/build.dart'; -import 'widgets/app_bar.dart'; -import 'widgets/error_brook_watcher.dart'; -import 'widgets/filter_property_sheet.dart'; -import 'widgets/task_box.dart'; -import 'widgets/task_grid.dart'; -import 'package:flutter_app_icons/flutter_app_icons.dart'; - -/// Shows information about the current build status of flutter/flutter. -/// -/// The tree's current build status is reflected in [AppBar]. -/// The results from tasks run on individual commits is shown in [TaskGrid]. -class BuildDashboardPage extends StatefulWidget { - const BuildDashboardPage({ - super.key, - this.queryParameters, - }); - - static const String routeName = '/build'; - - final Map? queryParameters; - - @override - State createState() => BuildDashboardPageState(); -} - -class BuildDashboardPageState extends State { - TaskGridFilter? _filter; - TaskGridFilter? _settingsBasis; - bool _smallScreen = false; - double screenWidthThreshold = 600; - final _flutterAppIconsPlugin = FlutterAppIcons(); - - /// Current Flutter repository to view. - String? repo; - - /// Git branch in [repo] to view. - /// - /// The frontend will update default branches based on [defaultBranches]. This enables users to easily switch from - /// master on one repo, to main for a different repo. - String? branch; - - /// Example branch for [truncate]. - /// - /// Include the ellipsis to get the correct length that should be truncated at. - final String _exampleBranch = 'flutter-3.12-candidate.23...'; - - @override - void initState() { - super.initState(); - if (widget.queryParameters != null) { - repo = widget.queryParameters!['repo'] ?? 'flutter'; - branch = widget.queryParameters!['branch'] ?? 'master'; - } - repo ??= 'flutter'; - branch ??= 'master'; - if (branch == 'master' || branch == 'main') { - branch = defaultBranches[repo!]; - } - _filter = TaskGridFilter.fromMap(widget.queryParameters); - _filter!.addListener(() { - setState(() {}); - }); - } - - @override - void dispose() { - _flutterAppIconsPlugin.setIcon(icon: 'favicon.png'); - super.dispose(); - } - - /// Convert the fields from this class into a URL. - /// - /// This enables bookmarking state specific values, like [repo]. - void _updateNavigation(BuildContext context) { - final Map queryParameters = {}; - if (widget.queryParameters != null) { - queryParameters.addAll(widget.queryParameters!); - } - if (_filter != null) { - queryParameters.addAll(_filter!.toMap()); - } - queryParameters['repo'] = repo!; - - queryParameters['branch'] = branch!; - - final Uri uri = Uri( - path: BuildDashboardPage.routeName, - queryParameters: queryParameters, - ); - Navigator.pushNamed(context, uri.toString()); - } - - void _removeSettingsDialog() { - setState(() { - _settingsBasis = null; - }); - } - - void _showSettingsDialog() { - setState(() { - _settingsBasis = TaskGridFilter.fromMap(_filter!.toMap()); - }); - } - - Widget _settingsDialog(BuildContext context, BuildState buildState) { - final ThemeData theme = Theme.of(context); - return Center( - child: Container( - decoration: BoxDecoration( - color: theme.dialogBackgroundColor.withAlpha(0xe0), - borderRadius: BorderRadius.circular(20.0), - ), - child: Material( - color: Colors.transparent, - child: FocusTraversalGroup( - child: SizedBox( - width: 500, - height: 360, - child: ListView( - children: [ - if (_smallScreen) ..._buildRepositorySelectionWidgets(context, buildState), - AnimatedBuilder( - animation: buildState, - builder: (context, child) { - final bool isAuthenticated = buildState.authService.isAuthenticated; - return TextButton( - onPressed: isAuthenticated ? buildState.refreshGitHubCommits : null, - child: child!, - ); - }, - child: const Text('Refresh GitHub Commits'), - ), - Row( - children: [ - Expanded(child: Center(child: FilterPropertySheet(_filter))), - ], - ), - Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - TextButton( - onPressed: _filter!.isDefault ? null : () => _filter!.reset(), - child: const Text('Defaults'), - ), - TextButton( - onPressed: _filter == _settingsBasis ? null : () => _updateNavigation(context), - child: const Text('Apply'), - ), - TextButton( - child: const Text('Cancel'), - onPressed: () { - if (_filter != _settingsBasis) { - _filter!.reset(); - _filter!.applyMap(_settingsBasis!.toMap()); - } - _removeSettingsDialog(); - }, - ), - ], - ), - ], - ), - ), - ), - ), - ), - ); - } - - /// List of widgets for selecting slug and branch for configuring the build view. - List _buildRepositorySelectionWidgets(BuildContext context, BuildState buildState) { - final ThemeData theme = Theme.of(context); - return [ - const Padding( - padding: EdgeInsets.only(top: 22, left: 5, right: 5), - child: Text( - 'repo: ', - textAlign: TextAlign.center, - ), - ), - DropdownButton( - key: const Key('repo dropdown'), - isExpanded: _smallScreen, - value: buildState.currentRepo, - icon: const Padding( - padding: EdgeInsets.only(top: 8), - child: Icon( - Icons.arrow_downward, - ), - ), - iconSize: 24, - elevation: 16, - underline: Container( - height: 2, - ), - onChanged: (String? selectedRepo) { - repo = selectedRepo; - _updateNavigation(context); - }, - items: buildState.repos.map>((String value) { - return DropdownMenuItem( - value: value, - child: Padding( - padding: const EdgeInsets.only(top: 11), - child: Center( - child: Text( - value, - style: theme.primaryTextTheme.bodyLarge, - textAlign: TextAlign.center, - ), - ), - ), - ); - }).toList(), - ), - const Padding( - padding: EdgeInsets.only(top: 22, left: 5, right: 5), - child: Text( - 'branch: ', - textAlign: TextAlign.center, - ), - ), - DropdownButton( - key: const Key('branch dropdown'), - isExpanded: _smallScreen, - value: buildState.currentBranch, - icon: const Padding( - padding: EdgeInsets.only(top: 8.0), - child: Icon( - Icons.arrow_downward, - ), - ), - iconSize: 24, - elevation: 16, - underline: Container( - height: 2, - ), - onChanged: (String? selectedBranch) { - branch = selectedBranch; - _updateNavigation(context); - }, - items: [ - DropdownMenuItem( - value: buildState.currentBranch, - child: Padding( - padding: const EdgeInsets.only(top: 9.0), - child: Center( - child: Text( - truncate(buildState.currentBranch, _exampleBranch.length), - style: theme.primaryTextTheme.bodyLarge, - ), - ), - ), - ), - ...buildState.branches - .where((Branch b) => b.branch != buildState.currentBranch) - .map>((Branch b) { - final String branchPrefix = (b.channel != 'HEAD') ? '${b.channel}: ' : ''; - return DropdownMenuItem( - value: b.branch, - child: Padding( - padding: const EdgeInsets.only(top: 9.0), - child: Center( - child: Text( - branchPrefix + truncate(b.branch, _exampleBranch.length), - style: theme.primaryTextTheme.bodyLarge, - ), - ), - ), - ); - }), - ], - ), - const Padding(padding: EdgeInsets.symmetric(horizontal: 4)), - ]; - } - - PopupMenuItem _getTaskKeyEntry({required Widget box, required String description}) { - return PopupMenuItem( - value: description, - child: Wrap( - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - const SizedBox(width: 10.0), - SizedBox.fromSize(size: Size.square(TaskBox.of(context)), child: box), - const SizedBox(width: 10.0), - Text(description), - ], - ), - ); - } - - List> _getTaskKey(bool isDark) { - final List> key = >[]; - - for (final String status in TaskBox.statusColor.keys) { - key.add( - _getTaskKeyEntry( - box: Container(color: TaskBox.statusColor[status]), - description: status, - ), - ); - key.add(const PopupMenuDivider()); - } - - key.add( - _getTaskKeyEntry( - box: Center( - child: Container( - width: TaskBox.of(context) * 0.8, - height: TaskBox.of(context) * 0.8, - decoration: BoxDecoration( - border: Border.all( - width: 2.0, - color: isDark ? Colors.white : Colors.black, - ), - ), - ), - ), - description: 'Flaky', - ), - ); - - key.add(const PopupMenuDivider()); - - key.add( - _getTaskKeyEntry( - box: const Center( - child: Text( - '!', - style: TextStyle( - fontSize: 24.0, - fontWeight: FontWeight.bold, - ), - ), - ), - description: 'Ran more than once', - ), - ); - - key.add(const PopupMenuDivider()); - - return key; - } - - String _getStatusTitle(BuildState? buildState) { - if (buildState == null || buildState.isTreeBuilding == null) { - return 'Loading...'; - } - if (buildState.isTreeBuilding!) { - return 'Tree is Open'; - } else { - if (buildState.failingTasks.isNotEmpty) { - return 'Tree is Closed (failing: ${buildState.failingTasks.join(', ')})'; - } else { - return 'Tree is Closed'; - } - } - } - - void _updatePage(BuildContext context, String newRepo, String newBranch) { - repo = newRepo; - branch = newBranch; - _updateNavigation(context); - } - - @override - Widget build(BuildContext context) { - final bool isDark = Theme.of(context).brightness == Brightness.dark; - final MediaQueryData queryData = MediaQuery.of(context); - final double devicePixelRatio = queryData.devicePixelRatio; - _smallScreen = queryData.size.width * devicePixelRatio < screenWidthThreshold; - - /// Color of [AppBar] based on [buildState.isTreeBuilding]. - final Map colorTable = { - null: Colors.grey[850], - false: isDark ? Colors.red[800] : Colors.red, - true: isDark ? Colors.green[800] : Colors.green, - }; - - final Uri flutterIssueUrl = Uri.parse( - 'https://github.com/flutter/flutter/issues/new?assignees=&labels=team-infra&projects=&template=6_infrastructure.yml', - ); - final BuildState buildState = Provider.of(context); - buildState.updateCurrentRepoBranch(repo!, branch!); - return CallbackShortcuts( - bindings: { - const SingleActivator(LogicalKeyboardKey.arrowUp): () => _updatePage(context, 'flutter', 'master'), - const SingleActivator(LogicalKeyboardKey.arrowDown): () => _updatePage(context, 'engine', 'main'), - const SingleActivator(LogicalKeyboardKey.arrowLeft): () => _updatePage(context, 'cocoon', 'main'), - const SingleActivator(LogicalKeyboardKey.arrowRight): () => _updatePage(context, 'packages', 'main'), - }, - child: Focus( - autofocus: true, - child: AnimatedBuilder( - animation: buildState, - builder: (BuildContext context, Widget? child) => Scaffold( - appBar: CocoonAppBar( - title: Tooltip( - message: _getStatusTitle(buildState), - child: Text( - _getStatusTitle(buildState), - ), - ), - backgroundColor: colorTable[buildState.isTreeBuilding], - actions: [ - if (!_smallScreen) ..._buildRepositorySelectionWidgets(context, buildState), - IconButton( - tooltip: 'Report Issue', - icon: const Icon(Icons.bug_report), - onPressed: () async { - if (await canLaunchUrl(flutterIssueUrl)) { - await launchUrl(flutterIssueUrl); - } else { - throw 'Could not launch $flutterIssueUrl'; - } - }, - ), - PopupMenuButton( - tooltip: 'Task Status Key', - child: const Icon(Icons.info_outline), - itemBuilder: (BuildContext context) => _getTaskKey(isDark), - ), - IconButton( - tooltip: 'Settings', - icon: const Icon(Icons.settings), - onPressed: _settingsBasis == null ? () => _showSettingsDialog() : null, - ), - ], - ), - body: ErrorBrookWatcher( - errors: buildState.errors, - child: Stack( - children: [ - SizedBox.expand( - child: TaskGridContainer( - filter: _filter, - useAnimatedLoading: true, - ), - ), - if (_settingsBasis != null) _settingsDialog(context, buildState), - ], - ), - ), - drawer: const DashboardNavigationDrawer(), - ), - ), - ), - ); - } -} diff --git a/dashboard/lib/dashboard_navigation_drawer.dart b/dashboard/lib/dashboard_navigation_drawer.dart deleted file mode 100644 index b78d2eda3..000000000 --- a/dashboard/lib/dashboard_navigation_drawer.dart +++ /dev/null @@ -1,44 +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:flutter/material.dart'; - -import 'logic/links.dart'; - -/// Sidebar for navigating the different pages of Cocoon. -class DashboardNavigationDrawer extends StatelessWidget { - const DashboardNavigationDrawer({super.key}); - - @override - Widget build(BuildContext context) { - final List cocoonLinks = createCocoonLinks(context); - final String? currentRoute = ModalRoute.of(context)!.settings.name; - return Drawer( - child: ListView( - children: [ - const DrawerHeader( - decoration: FlutterLogoDecoration( - margin: EdgeInsets.only(bottom: 24.0), - ), - child: Align( - alignment: Alignment.bottomCenter, - child: Text('Flutter Build Dashboard — Cocoon'), - ), - ), - ...cocoonLinks.map( - (CocoonLink link) => ListTile( - leading: link.icon, - title: Text(link.name!), - onTap: link.action, - selected: currentRoute == link.route, - ), - ), - const AboutListTile( - icon: FlutterLogo(), - ), - ], - ), - ); - } -} diff --git a/dashboard/lib/logic/brooks.dart b/dashboard/lib/logic/brooks.dart deleted file mode 100644 index 73034002b..000000000 --- a/dashboard/lib/logic/brooks.dart +++ /dev/null @@ -1,80 +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:flutter/foundation.dart'; - -typedef BrookCallback = void Function(T event); - -/// A source of events of type T. -/// -/// This is a light-weight stream, hence the name of the class. -/// -/// Listeners cannot be registered multiple times simultaneously. -class Brook { - Brook(); - - final Set> _listeners = >{}; - - void addListener(BrookCallback listener) { - assert(!_listeners.contains(listener)); - _listeners.add(listener); - } - - void removeListener(BrookCallback listener) { - assert(_listeners.contains(listener)); - _listeners.remove(listener); - } -} - -/// A place to send events of type T. -/// -/// Instances of this class can be given to consumers, as the type [Brook]. -/// This allows consumers to register for events without being able to send -/// events. -class BrookSink extends Brook { - BrookSink(); - - void send(T event) { - final List> frozenListeners = _listeners.toList(); - for (final BrookCallback listener in frozenListeners) { - try { - if (_listeners.contains(listener)) { - listener(event); - } - } catch (exception, stack) { - FlutterError.reportError( - FlutterErrorDetails( - exception: exception, - stack: stack, - library: 'Flutter Dashboard', - context: ErrorDescription('while sending event'), - informationCollector: () sync* { - yield DiagnosticsProperty>( - 'The $runtimeType sending the event was', - this, - style: DiagnosticsTreeStyle.errorProperty, - ); - yield DiagnosticsProperty( - 'The $T event was', - event, - style: DiagnosticsTreeStyle.errorProperty, - ); - }, - ), - ); - } - } - } -} - -class ErrorSink extends BrookSink { - ErrorSink(); - - @override - void send(String event) { - // TODO(ianh): log errors to a service as well, https://github.com/flutter/flutter/issues/52697 - debugPrint(event); // to allow for copy/paste - super.send(event); - } -} diff --git a/dashboard/lib/logic/links.dart b/dashboard/lib/logic/links.dart deleted file mode 100644 index 591479da5..000000000 --- a/dashboard/lib/logic/links.dart +++ /dev/null @@ -1,65 +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:flutter/material.dart'; -import 'package:url_launcher/url_launcher.dart'; - -import '../build_dashboard_page.dart'; - -/// List of links that are shown in the [DashboardNavigationDrawer]. -List createCocoonLinks(BuildContext context) { - return [ - CocoonLink( - name: 'Build', - route: BuildDashboardPage.routeName, - icon: const Icon(Icons.build), - action: () => Navigator.pushReplacementNamed(context, BuildDashboardPage.routeName), - ), - CocoonLink( - name: 'Framework Benchmarks', - icon: const Icon(Icons.show_chart), - action: () { - launchUrl(Uri.parse('https://flutter-flutter-perf.skia.org/')); - }, - ), - CocoonLink( - name: 'Engine Benchmarks', - icon: const Icon(Icons.show_chart), - action: () { - launchUrl(Uri.parse('https://flutter-engine-perf.skia.org/')); - }, - ), - CocoonLink( - name: 'Source Code', - icon: const Icon(Icons.code), - action: () { - launchUrl(Uri.parse('https://github.com/flutter/cocoon')); - }, - ), - ]; -} - -/// Data class for storing links on the Cocoon app. -class CocoonLink { - const CocoonLink({ - this.name, - this.route, - this.action, - this.icon, - }); - - /// Text shown to users describing this link. - final String? name; - - /// If the link is internal to this Flutter app, this can be passed to highlight on the [DashboardNavigationDrawer] the page the user is on. - final String? route; - - /// An [Icon] to represent this link. - final Icon? icon; - - /// Callback for when the link is activated. - /// - /// Can be used to redirect to internal or external routes. Will have acess to the [BuildContext]. - final void Function()? action; -} diff --git a/dashboard/lib/logic/qualified_task.dart b/dashboard/lib/logic/qualified_task.dart deleted file mode 100644 index ce08478a4..000000000 --- a/dashboard/lib/logic/qualified_task.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 'package:flutter/foundation.dart'; - -import '../model/task.pb.dart'; - -/// [Task.stageName] that maps to StageName enums. -// TODO(chillers): Remove these and use StageName enum when available. https://github.com/flutter/cocoon/issues/441 -class StageName { - static const String cirrus = 'cirrus'; - static const String cocoon = 'cocoon'; - static const String legacyLuci = 'chromebot'; - static const String luci = 'luci'; - static const String googleTest = 'google_internal'; - static const String dartInternal = 'dart-internal'; -} - -/// Base URLs for various endpoints that can relate to a [Task]. -const String _luciUrl = 'https://ci.chromium.org/p/flutter'; -const String _googleTestUrl = 'https://flutter-rob.corp.google.com'; -const String _dartInternalUrl = 'https://ci.chromium.org/p/dart-internal'; - -@immutable -class QualifiedTask { - const QualifiedTask({this.stage, this.task, this.pool}); - - QualifiedTask.fromTask(Task task) - : stage = task.stageName, - task = task.builderName, - pool = task.isFlaky ? 'luci.flutter.staging' : 'luci.flutter.prod'; - - final String? pool; - final String? stage; - final String? task; - - /// Get the URL for the configuration of this task. - /// - /// Luci tasks are stored on Luci. - String get sourceConfigurationUrl { - assert(isLuci || isGoogleTest || isDartInternal); - if (isLuci) { - return '$_luciUrl/builders/$pool/$task'; - } else if (isGoogleTest) { - return _googleTestUrl; - } else if (isDartInternal) { - return '$_dartInternalUrl/builders/$pool/$task'; - } - throw Exception('Failed to get source configuration url for $stage.'); - } - - /// Whether this task was run on google test. - bool get isGoogleTest => stage == StageName.googleTest; - - /// Whether the task was run on the LUCI infrastructure. - bool get isLuci => stage == StageName.cocoon || stage == StageName.legacyLuci || stage == StageName.luci; - - /// Whether this task was run on internal infrastructure (example: luci dart-internal). - bool get isDartInternal => stage == StageName.dartInternal; - - @override - bool operator ==(Object other) { - if (other.runtimeType != runtimeType) { - return false; - } - - if (isLuci) { - return other is QualifiedTask && other.task == task; - } - - return other is QualifiedTask && other.stage == stage && other.task == task; - } - - @override - int get hashCode { - // Ensure tasks from Cocoon or LUCI share the same columns. - if (isLuci) { - return StageName.cocoon.hashCode ^ task.hashCode; - } - - return stage.hashCode ^ task.hashCode; - } -} diff --git a/dashboard/lib/logic/task_grid_filter.dart b/dashboard/lib/logic/task_grid_filter.dart deleted file mode 100644 index 254bb4e4e..000000000 --- a/dashboard/lib/logic/task_grid_filter.dart +++ /dev/null @@ -1,281 +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:collection'; - -import 'package:flutter/material.dart'; -import 'package:collection/collection.dart'; -import '../logic/qualified_task.dart'; -import '../model/commit_status.pb.dart'; -import '../widgets/filter_property_sheet.dart'; - -/// A filter object for controlling which entries are visible in the Build dashboard grid -/// of tasks. This filter object can filter on a number of properties of the tasks including -/// the name of the task, information on the commit that generated the tasks, and platform -/// stage type toggles. -class TaskGridFilter extends FilterPropertySource { - TaskGridFilter(); - - factory TaskGridFilter.fromMap(Map? valueMap) => TaskGridFilter()..applyMap(valueMap); - - /// True iff all of the properties of the filter are set to their default values. - bool get isDefault => - _allProperties.values.toList().every((ValueFilterProperty element) => element.isDefault); - - /// Sets all properties of this filter to their default values. - void reset() { - for (final ValueFilterProperty property in _allProperties.values) { - property.reset(); - } - } - - /// Modifies this filter based on the values in the map. If the map is null, no changes are - /// made. All keys in the [valueMap] must match one of the field names of the filter properties. - void applyMap(Map? valueMap) { - if (valueMap != null) { - for (final MapEntry mapEntry in valueMap.entries) { - if (_allProperties.containsKey(mapEntry.key)) { - _allProperties[mapEntry.key]!.stringValue = mapEntry.value; - } - } - } - } - - final RegExpFilterProperty _taskProperty = - RegExpFilterProperty(fieldName: 'taskFilter', label: 'Task Name', caseSensitive: false); - final RegExpFilterProperty _authorProperty = - RegExpFilterProperty(fieldName: 'authorFilter', label: 'Commit Author', caseSensitive: false); - final RegExpFilterProperty _messageProperty = - RegExpFilterProperty(fieldName: 'messageFilter', label: 'Commit Message', caseSensitive: false); - final RegExpFilterProperty _hashProperty = RegExpFilterProperty(fieldName: 'hashFilter', label: 'Commit Hash'); - final BoolFilterProperty _macProperty = BoolFilterProperty(fieldName: 'showMac', label: 'Mac'); - final BoolFilterProperty _windowsPorperty = BoolFilterProperty(fieldName: 'showWindows', label: 'Windows'); - final BoolFilterProperty _iosProperty = BoolFilterProperty(fieldName: 'showiOS', label: 'iOS'); - final BoolFilterProperty _linuxPorperty = BoolFilterProperty(fieldName: 'showLinux', label: 'Linux'); - final BoolFilterProperty _androidProperty = BoolFilterProperty(fieldName: 'showAndroid', label: 'Android'); - final BoolFilterProperty _stagingProperty = - BoolFilterProperty(fieldName: 'showStaging', label: 'Staging', value: false); - - // [_allProperties] is a LinkedHashMap so we can trust its iteration order - LinkedHashMap>? _allPropertiesMap; - - LinkedHashMap> get _allProperties => - (_allPropertiesMap ??= (>{} - ..[_taskProperty.fieldName] = _taskProperty - ..[_authorProperty.fieldName] = _authorProperty - ..[_messageProperty.fieldName] = _messageProperty - ..[_hashProperty.fieldName] = _hashProperty - ..[_macProperty.fieldName] = _macProperty - ..[_windowsPorperty.fieldName] = _windowsPorperty - ..[_iosProperty.fieldName] = _iosProperty - ..[_linuxPorperty.fieldName] = _linuxPorperty - ..[_androidProperty.fieldName] = _androidProperty - ..[_stagingProperty.fieldName] = _stagingProperty) as LinkedHashMap>?)!; - - /// The [taskFilter] property is a regular expression that must match the name of the - /// task in the grid. This property will filter out columns on the build dashboard. - RegExp? get taskFilter => _taskProperty.regExp; - - set taskFilter(RegExp? regExp) => _taskProperty.regExp = regExp; - - /// The [authorFilter] property is a regular expression that must match the name of the - /// author of the task's commit. This property will filter out rows on the build dashboard. - RegExp? get authorFilter => _authorProperty.regExp; - - set authorFilter(RegExp? regExp) => _authorProperty.regExp = regExp; - - /// The [messageFilter] property is a regular expression that must match the commit message - /// of the task's commit. This property will filter out rows on the build dashboard. - RegExp? get messageFilter => _messageProperty.regExp; - - set messageFilter(RegExp? regExp) => _messageProperty.regExp = regExp; - - /// The [hashFilter] property is a regular expression that must match the hash of the - /// task's commit. This property will filter out rows on the build dashboard. - RegExp? get hashFilter => _hashProperty.regExp; - - set hashFilter(RegExp? regExp) => _hashProperty.regExp = regExp; - - /// The [showWindows] property is a boolean - /// - /// it indicates whether to display tasks produced by a Windows stage in the devicelab. - /// This property will filter out columns on the build dashboard. - bool? get showWindows => _windowsPorperty.value; - - set showWindows(bool? value) => _windowsPorperty.value = value; - - /// The [showMac] property is a boolean - /// - /// it indicates whether to display tasks produced by a Mac stage in the devicelab. - /// This property will filter out columns on the build dashboard. - bool? get showMac => _macProperty.value; - - set showMac(bool? value) => _macProperty.value = value; - - /// The [showiOS] property is a boolean - /// - /// it indicates whether to display tasks produced by an iOS stage in the devicelab. - /// This property will filter out columns on the build dashboard. - bool? get showiOS => _iosProperty.value; - - set showiOS(bool? value) => _iosProperty.value = value; - - /// The [showLinux] property is a boolean - /// - /// it indicates whether to display tasks produced by a Linux stage in the devicelab. - /// This property will filter out columns on the build dashboard. - bool? get showLinux => _linuxPorperty.value; - - set showLinux(bool? value) => _linuxPorperty.value = value; - - /// The [showAndroid] property is a boolean - /// - /// it indicates whether to display tasks produced by an Android stage in the devicelab. - /// This property will filter out columns on the build dashboard. - bool? get showAndroid => _androidProperty.value; - - set showAndroid(bool? value) => _androidProperty.value = value; - - /// The [showStaging] property is a boolean - /// - /// it indicates whether to display staging tasks (tasks with name prefixed - /// with linux_staging_build). - /// This property will filter out columns on the build dashboard. - bool? get showStaging => _stagingProperty.value; - - set showStaging(bool? value) => _stagingProperty.value = value; - - /// Check the values in the [CommitStatus] for compatibility with the properties of this - /// filter and return [true] iff the commit row should be displayed. - bool matchesCommit(CommitStatus commitStatus) { - if (!_authorProperty.matches(commitStatus.commit.author)) { - return false; - } - if (!_messageProperty.matches(commitStatus.commit.message)) { - return false; - } - if (!_hashProperty.matches(commitStatus.commit.sha)) { - return false; - } - return true; - } - - /// Check the values in the [QualifiedTask] for compatibility with the - /// properties of this filter and return [true] iff the commit column should be displayed. - bool matchesTask(QualifiedTask qualifiedTask) { - if (!_taskProperty.matches(qualifiedTask.task!)) { - return false; - } - - if ((!_allProperties['showStaging']?.value) && qualifiedTask.task!.toLowerCase().startsWith('staging_build_')) { - return false; - } - - final LinkedHashMap orderedOSFilter = LinkedHashMap.of({ - 'ios': _allProperties['showiOS']?.value ?? false, - 'android': _allProperties['showAndroid']?.value ?? false, - 'mac': _allProperties['showMac']?.value ?? false, - 'windows': _allProperties['showWindows']?.value ?? false, - 'linux': _allProperties['showLinux']?.value ?? false, - }); - return orderedOSFilter.entries - .firstWhereOrNull((MapEntry os) => qualifiedTask.task!.toLowerCase().contains(os.key)) - ?.value ?? - true; // Unrecognized stages always pass. - } - - /// Convert the filter into a String map (with or without default values populated) that - /// can be used to reconstruct the filter using the [fromMap] constructor and/or inject - /// its data into a JSON file or URL query parameter list. - Map toMap() => Map.fromEntries( - _allProperties.entries - .where( - (MapEntry> element) => !element.value.isDefault, - ) - .map( - (MapEntry> e) => - MapEntry(e.key, e.value.stringValue), - ), - ); - - /// A string useful for including in a URL as query parameters. The returned string will - /// include only non-default filter values separated by the URL parameter separator (`&`). - /// The string will not include the leading `?` character used to introduce URL parameters - /// in case this string must be mixed with other query parameters. - String get queryParameters => - toMap().entries.map((MapEntry e) => '${e.key}=${e.value}').join('&'); - - List? _layout; - - /// Return the list of properties of this filter in a form that can be used by a - /// [FilterPropertySheet] to display the fields to a user and allow them to edit the values. - @override - List get sheetLayout => _layout ??= [ - _taskProperty, - _authorProperty, - _messageProperty, - _hashProperty, - BoolFilterPropertyGroup( - label: 'Stages', - members: [ - _androidProperty, - _stagingProperty, - _iosProperty, - _linuxPorperty, - _macProperty, - _windowsPorperty, - ], - ), - ]; - - // [_allProperties] is a LinkedHashMap so we can trust its iteration order - String get _values => _allProperties.values - .where((ValueFilterProperty element) => !element.isDefault) - .map((ValueFilterProperty e) => e.stringValue) - .join(', '); - - @override - String toString() => 'TaskGridFilter($_values)'; - - // [_allProperties] is a LinkedHashMap so we can trust its iteration order - @override - int get hashCode => Object.hashAll(_allProperties.values.map((ValueFilterProperty e) => e.value)); - - @override - bool operator ==(Object other) { - if (other.runtimeType != runtimeType) { - return false; - } - return other is TaskGridFilter && - _allProperties.values.every( - (ValueFilterProperty element) => element.value == other._allProperties[element.fieldName]!.value, - ); - } - - List? _listeners; - - void notifyListeners() { - if (_listeners != null) { - for (final VoidCallback listener in _listeners!) { - listener(); - } - } - } - - @override - void addListener(VoidCallback listener) { - if (_listeners == null) { - _listeners = []; - for (final ValueFilterProperty property in _allProperties.values) { - property.addListener(notifyListeners); - } - } - _listeners!.add(listener); - } - - @override - void removeListener(VoidCallback listener) { - _listeners?.remove(listener); - } -} diff --git a/dashboard/lib/main.dart b/dashboard/lib/main.dart deleted file mode 100644 index 1c2565424..000000000 --- a/dashboard/lib/main.dart +++ /dev/null @@ -1,98 +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' if (kIsWeb) ''; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; -import 'package:flutter/services.dart'; - -import 'build_dashboard_page.dart'; -import 'service/cocoon.dart'; -import 'service/google_authentication.dart'; -import 'state/build.dart'; -import 'state/index.dart'; -import 'widgets/now.dart'; -import 'widgets/state_provider.dart'; - -void usage() { - // ignore: avoid_print - print(''' -Usage: cocoon [--use-production-service | --no-use-production-service] - - --[no-]use-production-service Enable/disable using the production Cocoon - service for source data. Defaults to the - production service in a release build, and the - fake service in a debug build. -'''); -} - -void main([List args = const []]) { - bool useProductionService = kReleaseMode; - if (args.contains('--help')) { - usage(); - if (!kIsWeb) { - exit(0); - } - } - if (args.contains('--use-production-service')) { - useProductionService = true; - } - if (args.contains('--no-use-production-service')) { - useProductionService = false; - } - final GoogleSignInService authService = GoogleSignInService(); - final CocoonService cocoonService = CocoonService(useProductionService: useProductionService); - runApp( - StateProvider( - signInService: authService, - indexState: IndexState(authService: authService), - buildState: BuildState(authService: authService, cocoonService: cocoonService), - child: Now(child: const MyApp()), - ), - ); - // Enable extensions like Vimium to traverse the dashboard - SemanticsBinding.instance.ensureSemantics(); -} - -class MyApp extends StatelessWidget { - const MyApp({super.key}); - - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Flutter Build Dashboard — Cocoon', - shortcuts: { - ...WidgetsApp.defaultShortcuts, - const SingleActivator(LogicalKeyboardKey.select): const ActivateIntent(), - }, - theme: ThemeData( - useMaterial3: false, - primaryTextTheme: const TextTheme( - bodyLarge: TextStyle(color: Colors.black87), - ), - ), - darkTheme: ThemeData.dark(), - initialRoute: BuildDashboardPage.routeName, - routes: { - BuildDashboardPage.routeName: (BuildContext context) => const BuildDashboardPage(), - }, - onGenerateRoute: (RouteSettings settings) { - final Uri uriData = Uri.parse(settings.name!); - if (uriData.path == BuildDashboardPage.routeName) { - return MaterialPageRoute( - settings: RouteSettings(name: uriData.toString()), - builder: (BuildContext context) { - return BuildDashboardPage( - queryParameters: uriData.queryParameters, - ); - }, - ); - } - return null; - }, - ); - } -} diff --git a/dashboard/lib/model/branch.pb.dart b/dashboard/lib/model/branch.pb.dart deleted file mode 100644 index 12c5902dc..000000000 --- a/dashboard/lib/model/branch.pb.dart +++ /dev/null @@ -1,115 +0,0 @@ -/// -// Generated code. Do not modify. -// source: lib/model/branch.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 Branch extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo( - const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Branch', - createEmptyInstance: create) - ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'branch') - ..aOS(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'repository') - ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'channel') - ..aInt64(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'lastActivity', - protoName: 'lastActivity') - ..hasRequiredFields = false; - - Branch._() : super(); - factory Branch({ - $core.String? branch, - $core.String? repository, - $core.String? channel, - $fixnum.Int64? lastActivity, - }) { - final _result = create(); - if (branch != null) { - _result.branch = branch; - } - if (repository != null) { - _result.repository = repository; - } - if (channel != null) { - _result.channel = channel; - } - if (lastActivity != null) { - _result.lastActivity = lastActivity; - } - return _result; - } - factory Branch.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory Branch.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') - Branch clone() => Branch()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - Branch copyWith(void Function(Branch) updates) => - super.copyWith((message) => updates(message as Branch)) as Branch; // ignore: deprecated_member_use - $pb.BuilderInfo get info_ => _i; - @$core.pragma('dart2js:noInline') - static Branch create() => Branch._(); - Branch createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Branch getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Branch? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get branch => $_getSZ(0); - @$pb.TagNumber(1) - set branch($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasBranch() => $_has(0); - @$pb.TagNumber(1) - void clearBranch() => clearField(1); - - @$pb.TagNumber(2) - $core.String get repository => $_getSZ(1); - @$pb.TagNumber(2) - set repository($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasRepository() => $_has(1); - @$pb.TagNumber(2) - void clearRepository() => clearField(2); - - @$pb.TagNumber(3) - $core.String get channel => $_getSZ(2); - @$pb.TagNumber(3) - set channel($core.String v) { - $_setString(2, v); - } - - @$pb.TagNumber(3) - $core.bool hasChannel() => $_has(2); - @$pb.TagNumber(3) - void clearChannel() => clearField(3); - - @$pb.TagNumber(4) - $fixnum.Int64 get lastActivity => $_getI64(3); - @$pb.TagNumber(4) - set lastActivity($fixnum.Int64 v) { - $_setInt64(3, v); - } - - @$pb.TagNumber(4) - $core.bool hasLastActivity() => $_has(3); - @$pb.TagNumber(4) - void clearLastActivity() => clearField(4); -} diff --git a/dashboard/lib/model/branch.pbenum.dart b/dashboard/lib/model/branch.pbenum.dart deleted file mode 100644 index 351ed102d..000000000 --- a/dashboard/lib/model/branch.pbenum.dart +++ /dev/null @@ -1,6 +0,0 @@ -/// -// Generated code. Do not modify. -// source: lib/model/branch.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/dashboard/lib/model/branch.pbjson.dart b/dashboard/lib/model/branch.pbjson.dart deleted file mode 100644 index bb88c102e..000000000 --- a/dashboard/lib/model/branch.pbjson.dart +++ /dev/null @@ -1,31 +0,0 @@ -/// -// Generated code. Do not modify. -// source: lib/model/branch.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 branchDescriptor instead') -const Branch$json = { - '1': 'Branch', - '2': [ - {'1': 'branch', '3': 1, '4': 1, '5': 9, '9': 0, '10': 'branch', '17': true}, - {'1': 'repository', '3': 2, '4': 1, '5': 9, '9': 1, '10': 'repository', '17': true}, - {'1': 'channel', '3': 3, '4': 1, '5': 9, '9': 2, '10': 'channel', '17': true}, - {'1': 'lastActivity', '3': 4, '4': 1, '5': 3, '9': 3, '10': 'lastActivity', '17': true}, - ], - '8': [ - {'1': '_branch'}, - {'1': '_repository'}, - {'1': '_channel'}, - {'1': '_lastActivity'}, - ], -}; - -/// Descriptor for `Branch`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List branchDescriptor = $convert.base64Decode( - 'CgZCcmFuY2gSGwoGYnJhbmNoGAEgASgJSABSBmJyYW5jaIgBARIjCgpyZXBvc2l0b3J5GAIgASgJSAFSCnJlcG9zaXRvcnmIAQESHQoHY2hhbm5lbBgDIAEoCUgCUgdjaGFubmVsiAEBEicKDGxhc3RBY3Rpdml0eRgEIAEoA0gDUgxsYXN0QWN0aXZpdHmIAQFCCQoHX2JyYW5jaEINCgtfcmVwb3NpdG9yeUIKCghfY2hhbm5lbEIPCg1fbGFzdEFjdGl2aXR5'); diff --git a/dashboard/lib/model/branch.pbserver.dart b/dashboard/lib/model/branch.pbserver.dart deleted file mode 100644 index 3c8a34c4f..000000000 --- a/dashboard/lib/model/branch.pbserver.dart +++ /dev/null @@ -1,8 +0,0 @@ -/// -// Generated code. Do not modify. -// source: lib/model/branch.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 'branch.pb.dart'; diff --git a/dashboard/lib/model/branch.proto b/dashboard/lib/model/branch.proto deleted file mode 100644 index 55aeb3267..000000000 --- a/dashboard/lib/model/branch.proto +++ /dev/null @@ -1,14 +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. - -syntax = "proto3"; - -package dashboard; - -message Branch { - optional string branch = 1; - optional string repository = 2; - optional string channel = 3; - optional int64 lastActivity = 4; -} diff --git a/dashboard/lib/model/build_status_response.pb.dart b/dashboard/lib/model/build_status_response.pb.dart deleted file mode 100644 index 9d6f83e6f..000000000 --- a/dashboard/lib/model/build_status_response.pb.dart +++ /dev/null @@ -1,79 +0,0 @@ -/// -// Generated code. Do not modify. -// source: lib/model/build_status_response.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: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', - createEmptyInstance: create) - ..e( - 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 createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuildStatusResponse getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(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/dashboard/lib/model/build_status_response.pbenum.dart b/dashboard/lib/model/build_status_response.pbenum.dart deleted file mode 100644 index 58eca86fc..000000000 --- a/dashboard/lib/model/build_status_response.pbenum.dart +++ /dev/null @@ -1,27 +0,0 @@ -/// -// Generated code. Do not modify. -// source: lib/model/build_status_response.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 - -// 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, $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'success'); - static const EnumBuildStatus failure = - EnumBuildStatus._(2, $core.bool.fromEnvironment('protobuf.omit_enum_names') ? '' : 'failure'); - - static const $core.List values = [ - 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/dashboard/lib/model/build_status_response.pbjson.dart b/dashboard/lib/model/build_status_response.pbjson.dart deleted file mode 100644 index 216c50316..000000000 --- a/dashboard/lib/model/build_status_response.pbjson.dart +++ /dev/null @@ -1,35 +0,0 @@ -/// -// Generated code. Do not modify. -// source: lib/model/build_status_response.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 enumBuildStatusDescriptor instead') -const EnumBuildStatus$json = { - '1': 'EnumBuildStatus', - '2': [ - {'1': 'success', '2': 1}, - {'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 = { - '1': 'BuildStatusResponse', - '2': [ - {'1': 'build_status', '3': 1, '4': 1, '5': 14, '6': '.EnumBuildStatus', '10': 'buildStatus'}, - {'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( - 'ChNCdWlsZFN0YXR1c1Jlc3BvbnNlEjMKDGJ1aWxkX3N0YXR1cxgBIAEoDjIQLkVudW1CdWlsZFN0YXR1c1ILYnVpbGRTdGF0dXMSIwoNZmFpbGluZ190YXNrcxgCIAMoCVIMZmFpbGluZ1Rhc2tz'); diff --git a/dashboard/lib/model/build_status_response.pbserver.dart b/dashboard/lib/model/build_status_response.pbserver.dart deleted file mode 100644 index b172e7964..000000000 --- a/dashboard/lib/model/build_status_response.pbserver.dart +++ /dev/null @@ -1,8 +0,0 @@ -/// -// Generated code. Do not modify. -// source: lib/model/build_status_response.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 'build_status_response.pb.dart'; diff --git a/dashboard/lib/model/build_status_response.proto b/dashboard/lib/model/build_status_response.proto deleted file mode 100644 index 964cca753..000000000 --- a/dashboard/lib/model/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 dashboard; - -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/dashboard/lib/model/commit.pb.dart b/dashboard/lib/model/commit.pb.dart deleted file mode 100644 index 580dd41e2..000000000 --- a/dashboard/lib/model/commit.pb.dart +++ /dev/null @@ -1,188 +0,0 @@ -/// -// Generated code. Do not modify. -// source: lib/model/commit.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; - -import 'key.pb.dart' as $0; - -class Commit extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo( - const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Commit', - createEmptyInstance: create) - ..aOM<$0.RootKey>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'key', - subBuilder: $0.RootKey.create) - ..aInt64(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'timestamp') - ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'sha') - ..aOS(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'author') - ..aOS(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'authorAvatarUrl', - protoName: 'authorAvatarUrl') - ..aOS(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'repository') - ..aOS(7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'branch') - ..aOS(8, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'message') - ..hasRequiredFields = false; - - Commit._() : super(); - factory Commit({ - $0.RootKey? key, - $fixnum.Int64? timestamp, - $core.String? sha, - $core.String? author, - $core.String? authorAvatarUrl, - $core.String? repository, - $core.String? branch, - $core.String? message, - }) { - final _result = create(); - if (key != null) { - _result.key = key; - } - if (timestamp != null) { - _result.timestamp = timestamp; - } - if (sha != null) { - _result.sha = sha; - } - if (author != null) { - _result.author = author; - } - if (authorAvatarUrl != null) { - _result.authorAvatarUrl = authorAvatarUrl; - } - if (repository != null) { - _result.repository = repository; - } - if (branch != null) { - _result.branch = branch; - } - if (message != null) { - _result.message = message; - } - return _result; - } - factory Commit.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory Commit.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') - Commit clone() => Commit()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - Commit copyWith(void Function(Commit) updates) => - super.copyWith((message) => updates(message as Commit)) as Commit; // ignore: deprecated_member_use - $pb.BuilderInfo get info_ => _i; - @$core.pragma('dart2js:noInline') - static Commit create() => Commit._(); - Commit createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Commit getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Commit? _defaultInstance; - - @$pb.TagNumber(1) - $0.RootKey get key => $_getN(0); - @$pb.TagNumber(1) - set key($0.RootKey v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasKey() => $_has(0); - @$pb.TagNumber(1) - void clearKey() => clearField(1); - @$pb.TagNumber(1) - $0.RootKey ensureKey() => $_ensure(0); - - @$pb.TagNumber(2) - $fixnum.Int64 get timestamp => $_getI64(1); - @$pb.TagNumber(2) - set timestamp($fixnum.Int64 v) { - $_setInt64(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasTimestamp() => $_has(1); - @$pb.TagNumber(2) - void clearTimestamp() => clearField(2); - - @$pb.TagNumber(3) - $core.String get sha => $_getSZ(2); - @$pb.TagNumber(3) - set sha($core.String v) { - $_setString(2, v); - } - - @$pb.TagNumber(3) - $core.bool hasSha() => $_has(2); - @$pb.TagNumber(3) - void clearSha() => clearField(3); - - @$pb.TagNumber(4) - $core.String get author => $_getSZ(3); - @$pb.TagNumber(4) - set author($core.String v) { - $_setString(3, v); - } - - @$pb.TagNumber(4) - $core.bool hasAuthor() => $_has(3); - @$pb.TagNumber(4) - void clearAuthor() => clearField(4); - - @$pb.TagNumber(5) - $core.String get authorAvatarUrl => $_getSZ(4); - @$pb.TagNumber(5) - set authorAvatarUrl($core.String v) { - $_setString(4, v); - } - - @$pb.TagNumber(5) - $core.bool hasAuthorAvatarUrl() => $_has(4); - @$pb.TagNumber(5) - void clearAuthorAvatarUrl() => clearField(5); - - @$pb.TagNumber(6) - $core.String get repository => $_getSZ(5); - @$pb.TagNumber(6) - set repository($core.String v) { - $_setString(5, v); - } - - @$pb.TagNumber(6) - $core.bool hasRepository() => $_has(5); - @$pb.TagNumber(6) - void clearRepository() => clearField(6); - - @$pb.TagNumber(7) - $core.String get branch => $_getSZ(6); - @$pb.TagNumber(7) - set branch($core.String v) { - $_setString(6, v); - } - - @$pb.TagNumber(7) - $core.bool hasBranch() => $_has(6); - @$pb.TagNumber(7) - void clearBranch() => clearField(7); - - @$pb.TagNumber(8) - $core.String get message => $_getSZ(7); - @$pb.TagNumber(8) - set message($core.String v) { - $_setString(7, v); - } - - @$pb.TagNumber(8) - $core.bool hasMessage() => $_has(7); - @$pb.TagNumber(8) - void clearMessage() => clearField(8); -} diff --git a/dashboard/lib/model/commit.pbenum.dart b/dashboard/lib/model/commit.pbenum.dart deleted file mode 100644 index a516d1158..000000000 --- a/dashboard/lib/model/commit.pbenum.dart +++ /dev/null @@ -1,6 +0,0 @@ -/// -// Generated code. Do not modify. -// source: lib/model/commit.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/dashboard/lib/model/commit.pbjson.dart b/dashboard/lib/model/commit.pbjson.dart deleted file mode 100644 index aa3883753..000000000 --- a/dashboard/lib/model/commit.pbjson.dart +++ /dev/null @@ -1,29 +0,0 @@ -/// -// Generated code. Do not modify. -// source: lib/model/commit.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 commitDescriptor instead') -const Commit$json = { - '1': 'Commit', - '2': [ - {'1': 'key', '3': 1, '4': 1, '5': 11, '6': '.RootKey', '10': 'key'}, - {'1': 'timestamp', '3': 2, '4': 1, '5': 3, '10': 'timestamp'}, - {'1': 'sha', '3': 3, '4': 1, '5': 9, '10': 'sha'}, - {'1': 'author', '3': 4, '4': 1, '5': 9, '10': 'author'}, - {'1': 'authorAvatarUrl', '3': 5, '4': 1, '5': 9, '10': 'authorAvatarUrl'}, - {'1': 'message', '3': 8, '4': 1, '5': 9, '10': 'message'}, - {'1': 'repository', '3': 6, '4': 1, '5': 9, '10': 'repository'}, - {'1': 'branch', '3': 7, '4': 1, '5': 9, '10': 'branch'}, - ], -}; - -/// Descriptor for `Commit`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List commitDescriptor = $convert.base64Decode( - 'CgZDb21taXQSGgoDa2V5GAEgASgLMgguUm9vdEtleVIDa2V5EhwKCXRpbWVzdGFtcBgCIAEoA1IJdGltZXN0YW1wEhAKA3NoYRgDIAEoCVIDc2hhEhYKBmF1dGhvchgEIAEoCVIGYXV0aG9yEigKD2F1dGhvckF2YXRhclVybBgFIAEoCVIPYXV0aG9yQXZhdGFyVXJsEhgKB21lc3NhZ2UYCCABKAlSB21lc3NhZ2USHgoKcmVwb3NpdG9yeRgGIAEoCVIKcmVwb3NpdG9yeRIWCgZicmFuY2gYByABKAlSBmJyYW5jaA=='); diff --git a/dashboard/lib/model/commit.pbserver.dart b/dashboard/lib/model/commit.pbserver.dart deleted file mode 100644 index 3a6498079..000000000 --- a/dashboard/lib/model/commit.pbserver.dart +++ /dev/null @@ -1,8 +0,0 @@ -/// -// Generated code. Do not modify. -// source: lib/model/commit.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 'commit.pb.dart'; diff --git a/dashboard/lib/model/commit.proto b/dashboard/lib/model/commit.proto deleted file mode 100644 index 451377d21..000000000 --- a/dashboard/lib/model/commit.proto +++ /dev/null @@ -1,21 +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 dashboard; - -import "lib/model/key.proto"; - -message Commit { - // Next ID: 9 - optional RootKey key = 1; - optional int64 timestamp = 2; - optional string sha = 3; - optional string author = 4; - optional string authorAvatarUrl = 5; - optional string message = 8; - optional string repository = 6; - optional string branch = 7; -} diff --git a/dashboard/lib/model/commit_status.pb.dart b/dashboard/lib/model/commit_status.pb.dart deleted file mode 100644 index 098b161ba..000000000 --- a/dashboard/lib/model/commit_status.pb.dart +++ /dev/null @@ -1,94 +0,0 @@ -/// -// Generated code. Do not modify. -// source: lib/model/commit_status.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:protobuf/protobuf.dart' as $pb; - -import 'commit.pb.dart' as $0; -import 'task.pb.dart' as $1; - -class CommitStatus extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo( - const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'CommitStatus', - createEmptyInstance: create) - ..aOM<$0.Commit>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'commit', - subBuilder: $0.Commit.create) - ..pc<$1.Task>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'tasks', $pb.PbFieldType.PM, - subBuilder: $1.Task.create) - ..aOS(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'branch') - ..hasRequiredFields = false; - - CommitStatus._() : super(); - factory CommitStatus({ - $0.Commit? commit, - $core.Iterable<$1.Task>? tasks, - $core.String? branch, - }) { - final _result = create(); - if (commit != null) { - _result.commit = commit; - } - if (tasks != null) { - _result.tasks.addAll(tasks); - } - if (branch != null) { - _result.branch = branch; - } - return _result; - } - factory CommitStatus.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory CommitStatus.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') - CommitStatus clone() => CommitStatus()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - CommitStatus copyWith(void Function(CommitStatus) updates) => - super.copyWith((message) => updates(message as CommitStatus)) as CommitStatus; // ignore: deprecated_member_use - $pb.BuilderInfo get info_ => _i; - @$core.pragma('dart2js:noInline') - static CommitStatus create() => CommitStatus._(); - CommitStatus createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static CommitStatus getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static CommitStatus? _defaultInstance; - - @$pb.TagNumber(1) - $0.Commit get commit => $_getN(0); - @$pb.TagNumber(1) - set commit($0.Commit v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasCommit() => $_has(0); - @$pb.TagNumber(1) - void clearCommit() => clearField(1); - @$pb.TagNumber(1) - $0.Commit ensureCommit() => $_ensure(0); - - @$pb.TagNumber(2) - $core.List<$1.Task> get tasks => $_getList(1); - - @$pb.TagNumber(3) - $core.String get branch => $_getSZ(2); - @$pb.TagNumber(3) - set branch($core.String v) { - $_setString(2, v); - } - - @$pb.TagNumber(3) - $core.bool hasBranch() => $_has(2); - @$pb.TagNumber(3) - void clearBranch() => clearField(3); -} diff --git a/dashboard/lib/model/commit_status.pbenum.dart b/dashboard/lib/model/commit_status.pbenum.dart deleted file mode 100644 index 48af52e8a..000000000 --- a/dashboard/lib/model/commit_status.pbenum.dart +++ /dev/null @@ -1,6 +0,0 @@ -/// -// Generated code. Do not modify. -// source: lib/model/commit_status.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/dashboard/lib/model/commit_status.pbjson.dart b/dashboard/lib/model/commit_status.pbjson.dart deleted file mode 100644 index 80cc9bdba..000000000 --- a/dashboard/lib/model/commit_status.pbjson.dart +++ /dev/null @@ -1,24 +0,0 @@ -/// -// Generated code. Do not modify. -// source: lib/model/commit_status.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 commitStatusDescriptor instead') -const CommitStatus$json = { - '1': 'CommitStatus', - '2': [ - {'1': 'commit', '3': 1, '4': 1, '5': 11, '6': '.Commit', '10': 'commit'}, - {'1': 'tasks', '3': 2, '4': 3, '5': 11, '6': '.Task', '10': 'tasks'}, - {'1': 'branch', '3': 3, '4': 1, '5': 9, '10': 'branch'}, - ], -}; - -/// Descriptor for `CommitStatus`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List commitStatusDescriptor = $convert.base64Decode( - 'CgxDb21taXRTdGF0dXMSHwoGY29tbWl0GAEgASgLMgcuQ29tbWl0UgZjb21taXQSGwoFdGFza3MYAiADKAsyBS5UYXNrUgV0YXNrcxIWCgZicmFuY2gYAyABKAlSBmJyYW5jaA=='); diff --git a/dashboard/lib/model/commit_status.pbserver.dart b/dashboard/lib/model/commit_status.pbserver.dart deleted file mode 100644 index 87a55061b..000000000 --- a/dashboard/lib/model/commit_status.pbserver.dart +++ /dev/null @@ -1,8 +0,0 @@ -/// -// Generated code. Do not modify. -// source: lib/model/commit_status.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 'commit_status.pb.dart'; diff --git a/dashboard/lib/model/commit_status.proto b/dashboard/lib/model/commit_status.proto deleted file mode 100644 index 9fb6c10d9..000000000 --- a/dashboard/lib/model/commit_status.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 dashboard; - -import "lib/model/commit.proto"; -import "lib/model/task.proto"; - -message CommitStatus { - optional Commit commit = 1; - repeated Task tasks = 2; - optional string branch = 3; -} diff --git a/dashboard/lib/model/key.pb.dart b/dashboard/lib/model/key.pb.dart deleted file mode 100644 index 87446c941..000000000 --- a/dashboard/lib/model/key.pb.dart +++ /dev/null @@ -1,194 +0,0 @@ -/// -// Generated code. Do not modify. -// source: lib/model/key.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; - -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', - 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(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 createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Key getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(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', - createEmptyInstance: create) - ..aOS(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'namespace') - ..aOM(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 createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static RootKey getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(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/dashboard/lib/model/key.pbenum.dart b/dashboard/lib/model/key.pbenum.dart deleted file mode 100644 index 3e2ceb99d..000000000 --- a/dashboard/lib/model/key.pbenum.dart +++ /dev/null @@ -1,6 +0,0 @@ -/// -// Generated code. Do not modify. -// source: lib/model/key.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/dashboard/lib/model/key.pbjson.dart b/dashboard/lib/model/key.pbjson.dart deleted file mode 100644 index 01379726d..000000000 --- a/dashboard/lib/model/key.pbjson.dart +++ /dev/null @@ -1,40 +0,0 @@ -/// -// Generated code. Do not modify. -// source: lib/model/key.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 keyDescriptor instead') -const Key$json = { - '1': 'Key', - '2': [ - {'1': 'type', '3': 1, '4': 1, '5': 9, '10': 'type'}, - {'1': 'uid', '3': 2, '4': 1, '5': 3, '9': 0, '10': 'uid'}, - {'1': 'name', '3': 3, '4': 1, '5': 9, '9': 0, '10': 'name'}, - {'1': 'child', '3': 4, '4': 1, '5': 11, '6': '.Key', '10': 'child'}, - ], - '8': [ - {'1': 'id'}, - ], -}; - -/// Descriptor for `Key`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List keyDescriptor = $convert.base64Decode( - 'CgNLZXkSEgoEdHlwZRgBIAEoCVIEdHlwZRISCgN1aWQYAiABKANIAFIDdWlkEhQKBG5hbWUYAyABKAlIAFIEbmFtZRIaCgVjaGlsZBgEIAEoCzIELktleVIFY2hpbGRCBAoCaWQ='); -@$core.Deprecated('Use rootKeyDescriptor instead') -const RootKey$json = { - '1': 'RootKey', - '2': [ - {'1': 'namespace', '3': 1, '4': 1, '5': 9, '10': 'namespace'}, - {'1': 'child', '3': 2, '4': 1, '5': 11, '6': '.Key', '10': 'child'}, - ], -}; - -/// Descriptor for `RootKey`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List rootKeyDescriptor = $convert - .base64Decode('CgdSb290S2V5EhwKCW5hbWVzcGFjZRgBIAEoCVIJbmFtZXNwYWNlEhoKBWNoaWxkGAIgASgLMgQuS2V5UgVjaGlsZA=='); diff --git a/dashboard/lib/model/key.pbserver.dart b/dashboard/lib/model/key.pbserver.dart deleted file mode 100644 index 4363f0f56..000000000 --- a/dashboard/lib/model/key.pbserver.dart +++ /dev/null @@ -1,8 +0,0 @@ -/// -// Generated code. Do not modify. -// source: lib/model/key.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.pb.dart'; diff --git a/dashboard/lib/model/key.proto b/dashboard/lib/model/key.proto deleted file mode 100644 index f56762cef..000000000 --- a/dashboard/lib/model/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 dashboard; - -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/dashboard/lib/model/task.pb.dart b/dashboard/lib/model/task.pb.dart deleted file mode 100644 index 6b4d58584..000000000 --- a/dashboard/lib/model/task.pb.dart +++ /dev/null @@ -1,376 +0,0 @@ -/// -// Generated code. Do not modify. -// source: lib/model/task.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; - -import 'key.pb.dart' as $0; - -class Task extends $pb.GeneratedMessage { - static final $pb.BuilderInfo _i = $pb.BuilderInfo( - const $core.bool.fromEnvironment('protobuf.omit_message_names') ? '' : 'Task', - createEmptyInstance: create) - ..aOM<$0.RootKey>(1, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'key', - subBuilder: $0.RootKey.create) - ..aOM<$0.RootKey>(2, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'commitKey', - subBuilder: $0.RootKey.create) - ..aInt64(3, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'createTimestamp') - ..aInt64(4, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'startTimestamp') - ..aInt64(5, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'endTimestamp') - ..aOS(6, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'name') - ..a<$core.int>( - 7, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'attempts', $pb.PbFieldType.O3) - ..aOB(8, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'isFlaky') - ..a<$core.int>( - 9, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'timeoutInMinutes', $pb.PbFieldType.O3) - ..aOS(10, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'reason') - ..pPS(11, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'requiredCapabilities') - ..aOS(12, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'reservedForAgentId', - protoName: 'reserved_for_agentId') - ..aOS(13, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'stageName') - ..aOS(14, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'status') - ..a<$core.int>( - 15, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'buildNumber', $pb.PbFieldType.O3, - protoName: 'buildNumber') - ..aOS(16, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'buildNumberList', - protoName: 'buildNumberList') - ..aOS(17, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'builderName', - protoName: 'builderName') - ..aOS(18, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'luciBucket', - protoName: 'luciBucket') - ..aOB(19, const $core.bool.fromEnvironment('protobuf.omit_field_names') ? '' : 'isTestFlaky') - ..hasRequiredFields = false; - - Task._() : super(); - factory Task({ - $0.RootKey? key, - $0.RootKey? commitKey, - $fixnum.Int64? createTimestamp, - $fixnum.Int64? startTimestamp, - $fixnum.Int64? endTimestamp, - $core.String? name, - $core.int? attempts, - $core.bool? isFlaky, - $core.int? timeoutInMinutes, - $core.String? reason, - $core.Iterable<$core.String>? requiredCapabilities, - $core.String? reservedForAgentId, - $core.String? stageName, - $core.String? status, - $core.int? buildNumber, - $core.String? buildNumberList, - $core.String? builderName, - $core.String? luciBucket, - $core.bool? isTestFlaky, - }) { - final _result = create(); - if (key != null) { - _result.key = key; - } - if (commitKey != null) { - _result.commitKey = commitKey; - } - if (createTimestamp != null) { - _result.createTimestamp = createTimestamp; - } - if (startTimestamp != null) { - _result.startTimestamp = startTimestamp; - } - if (endTimestamp != null) { - _result.endTimestamp = endTimestamp; - } - if (name != null) { - _result.name = name; - } - if (attempts != null) { - _result.attempts = attempts; - } - if (isFlaky != null) { - _result.isFlaky = isFlaky; - } - if (timeoutInMinutes != null) { - _result.timeoutInMinutes = timeoutInMinutes; - } - if (reason != null) { - _result.reason = reason; - } - if (requiredCapabilities != null) { - _result.requiredCapabilities.addAll(requiredCapabilities); - } - if (reservedForAgentId != null) { - _result.reservedForAgentId = reservedForAgentId; - } - if (stageName != null) { - _result.stageName = stageName; - } - if (status != null) { - _result.status = status; - } - if (buildNumber != null) { - _result.buildNumber = buildNumber; - } - if (buildNumberList != null) { - _result.buildNumberList = buildNumberList; - } - if (builderName != null) { - _result.builderName = builderName; - } - if (luciBucket != null) { - _result.luciBucket = luciBucket; - } - if (isTestFlaky != null) { - _result.isTestFlaky = isTestFlaky; - } - return _result; - } - factory Task.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory Task.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') - Task clone() => Task()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - Task copyWith(void Function(Task) updates) => - super.copyWith((message) => updates(message as Task)) as Task; // ignore: deprecated_member_use - $pb.BuilderInfo get info_ => _i; - @$core.pragma('dart2js:noInline') - static Task create() => Task._(); - Task createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Task getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Task? _defaultInstance; - - @$pb.TagNumber(1) - $0.RootKey get key => $_getN(0); - @$pb.TagNumber(1) - set key($0.RootKey v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasKey() => $_has(0); - @$pb.TagNumber(1) - void clearKey() => clearField(1); - @$pb.TagNumber(1) - $0.RootKey ensureKey() => $_ensure(0); - - @$pb.TagNumber(2) - $0.RootKey get commitKey => $_getN(1); - @$pb.TagNumber(2) - set commitKey($0.RootKey v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasCommitKey() => $_has(1); - @$pb.TagNumber(2) - void clearCommitKey() => clearField(2); - @$pb.TagNumber(2) - $0.RootKey ensureCommitKey() => $_ensure(1); - - @$pb.TagNumber(3) - $fixnum.Int64 get createTimestamp => $_getI64(2); - @$pb.TagNumber(3) - set createTimestamp($fixnum.Int64 v) { - $_setInt64(2, v); - } - - @$pb.TagNumber(3) - $core.bool hasCreateTimestamp() => $_has(2); - @$pb.TagNumber(3) - void clearCreateTimestamp() => clearField(3); - - @$pb.TagNumber(4) - $fixnum.Int64 get startTimestamp => $_getI64(3); - @$pb.TagNumber(4) - set startTimestamp($fixnum.Int64 v) { - $_setInt64(3, v); - } - - @$pb.TagNumber(4) - $core.bool hasStartTimestamp() => $_has(3); - @$pb.TagNumber(4) - void clearStartTimestamp() => clearField(4); - - @$pb.TagNumber(5) - $fixnum.Int64 get endTimestamp => $_getI64(4); - @$pb.TagNumber(5) - set endTimestamp($fixnum.Int64 v) { - $_setInt64(4, v); - } - - @$pb.TagNumber(5) - $core.bool hasEndTimestamp() => $_has(4); - @$pb.TagNumber(5) - void clearEndTimestamp() => clearField(5); - - @$pb.TagNumber(6) - $core.String get name => $_getSZ(5); - @$pb.TagNumber(6) - set name($core.String v) { - $_setString(5, v); - } - - @$pb.TagNumber(6) - $core.bool hasName() => $_has(5); - @$pb.TagNumber(6) - void clearName() => clearField(6); - - @$pb.TagNumber(7) - $core.int get attempts => $_getIZ(6); - @$pb.TagNumber(7) - set attempts($core.int v) { - $_setSignedInt32(6, v); - } - - @$pb.TagNumber(7) - $core.bool hasAttempts() => $_has(6); - @$pb.TagNumber(7) - void clearAttempts() => clearField(7); - - @$pb.TagNumber(8) - $core.bool get isFlaky => $_getBF(7); - @$pb.TagNumber(8) - set isFlaky($core.bool v) { - $_setBool(7, v); - } - - @$pb.TagNumber(8) - $core.bool hasIsFlaky() => $_has(7); - @$pb.TagNumber(8) - void clearIsFlaky() => clearField(8); - - @$pb.TagNumber(9) - $core.int get timeoutInMinutes => $_getIZ(8); - @$pb.TagNumber(9) - set timeoutInMinutes($core.int v) { - $_setSignedInt32(8, v); - } - - @$pb.TagNumber(9) - $core.bool hasTimeoutInMinutes() => $_has(8); - @$pb.TagNumber(9) - void clearTimeoutInMinutes() => clearField(9); - - @$pb.TagNumber(10) - $core.String get reason => $_getSZ(9); - @$pb.TagNumber(10) - set reason($core.String v) { - $_setString(9, v); - } - - @$pb.TagNumber(10) - $core.bool hasReason() => $_has(9); - @$pb.TagNumber(10) - void clearReason() => clearField(10); - - @$pb.TagNumber(11) - $core.List<$core.String> get requiredCapabilities => $_getList(10); - - @$pb.TagNumber(12) - $core.String get reservedForAgentId => $_getSZ(11); - @$pb.TagNumber(12) - set reservedForAgentId($core.String v) { - $_setString(11, v); - } - - @$pb.TagNumber(12) - $core.bool hasReservedForAgentId() => $_has(11); - @$pb.TagNumber(12) - void clearReservedForAgentId() => clearField(12); - - @$pb.TagNumber(13) - $core.String get stageName => $_getSZ(12); - @$pb.TagNumber(13) - set stageName($core.String v) { - $_setString(12, v); - } - - @$pb.TagNumber(13) - $core.bool hasStageName() => $_has(12); - @$pb.TagNumber(13) - void clearStageName() => clearField(13); - - @$pb.TagNumber(14) - $core.String get status => $_getSZ(13); - @$pb.TagNumber(14) - set status($core.String v) { - $_setString(13, v); - } - - @$pb.TagNumber(14) - $core.bool hasStatus() => $_has(13); - @$pb.TagNumber(14) - void clearStatus() => clearField(14); - - @$pb.TagNumber(15) - $core.int get buildNumber => $_getIZ(14); - @$pb.TagNumber(15) - set buildNumber($core.int v) { - $_setSignedInt32(14, v); - } - - @$pb.TagNumber(15) - $core.bool hasBuildNumber() => $_has(14); - @$pb.TagNumber(15) - void clearBuildNumber() => clearField(15); - - @$pb.TagNumber(16) - $core.String get buildNumberList => $_getSZ(15); - @$pb.TagNumber(16) - set buildNumberList($core.String v) { - $_setString(15, v); - } - - @$pb.TagNumber(16) - $core.bool hasBuildNumberList() => $_has(15); - @$pb.TagNumber(16) - void clearBuildNumberList() => clearField(16); - - @$pb.TagNumber(17) - $core.String get builderName => $_getSZ(16); - @$pb.TagNumber(17) - set builderName($core.String v) { - $_setString(16, v); - } - - @$pb.TagNumber(17) - $core.bool hasBuilderName() => $_has(16); - @$pb.TagNumber(17) - void clearBuilderName() => clearField(17); - - @$pb.TagNumber(18) - $core.String get luciBucket => $_getSZ(17); - @$pb.TagNumber(18) - set luciBucket($core.String v) { - $_setString(17, v); - } - - @$pb.TagNumber(18) - $core.bool hasLuciBucket() => $_has(17); - @$pb.TagNumber(18) - void clearLuciBucket() => clearField(18); - - @$pb.TagNumber(19) - $core.bool get isTestFlaky => $_getBF(18); - @$pb.TagNumber(19) - set isTestFlaky($core.bool v) { - $_setBool(18, v); - } - - @$pb.TagNumber(19) - $core.bool hasIsTestFlaky() => $_has(18); - @$pb.TagNumber(19) - void clearIsTestFlaky() => clearField(19); -} diff --git a/dashboard/lib/model/task.pbenum.dart b/dashboard/lib/model/task.pbenum.dart deleted file mode 100644 index db3247da7..000000000 --- a/dashboard/lib/model/task.pbenum.dart +++ /dev/null @@ -1,6 +0,0 @@ -/// -// Generated code. Do not modify. -// source: lib/model/task.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/dashboard/lib/model/task.pbjson.dart b/dashboard/lib/model/task.pbjson.dart deleted file mode 100644 index 72e4d08dc..000000000 --- a/dashboard/lib/model/task.pbjson.dart +++ /dev/null @@ -1,40 +0,0 @@ -/// -// Generated code. Do not modify. -// source: lib/model/task.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 taskDescriptor instead') -const Task$json = { - '1': 'Task', - '2': [ - {'1': 'key', '3': 1, '4': 1, '5': 11, '6': '.RootKey', '10': 'key'}, - {'1': 'commit_key', '3': 2, '4': 1, '5': 11, '6': '.RootKey', '10': 'commitKey'}, - {'1': 'create_timestamp', '3': 3, '4': 1, '5': 3, '10': 'createTimestamp'}, - {'1': 'start_timestamp', '3': 4, '4': 1, '5': 3, '10': 'startTimestamp'}, - {'1': 'end_timestamp', '3': 5, '4': 1, '5': 3, '10': 'endTimestamp'}, - {'1': 'name', '3': 6, '4': 1, '5': 9, '10': 'name'}, - {'1': 'attempts', '3': 7, '4': 1, '5': 5, '10': 'attempts'}, - {'1': 'is_flaky', '3': 8, '4': 1, '5': 8, '10': 'isFlaky'}, - {'1': 'timeout_in_minutes', '3': 9, '4': 1, '5': 5, '10': 'timeoutInMinutes'}, - {'1': 'reason', '3': 10, '4': 1, '5': 9, '10': 'reason'}, - {'1': 'required_capabilities', '3': 11, '4': 3, '5': 9, '10': 'requiredCapabilities'}, - {'1': 'reserved_for_agentId', '3': 12, '4': 1, '5': 9, '10': 'reservedForAgentId'}, - {'1': 'stage_name', '3': 13, '4': 1, '5': 9, '10': 'stageName'}, - {'1': 'status', '3': 14, '4': 1, '5': 9, '10': 'status'}, - {'1': 'buildNumber', '3': 15, '4': 1, '5': 5, '10': 'buildNumber'}, - {'1': 'buildNumberList', '3': 16, '4': 1, '5': 9, '10': 'buildNumberList'}, - {'1': 'builderName', '3': 17, '4': 1, '5': 9, '10': 'builderName'}, - {'1': 'luciBucket', '3': 18, '4': 1, '5': 9, '10': 'luciBucket'}, - {'1': 'is_test_flaky', '3': 19, '4': 1, '5': 8, '10': 'isTestFlaky'}, - ], -}; - -/// Descriptor for `Task`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List taskDescriptor = $convert.base64Decode( - 'CgRUYXNrEhoKA2tleRgBIAEoCzIILlJvb3RLZXlSA2tleRInCgpjb21taXRfa2V5GAIgASgLMgguUm9vdEtleVIJY29tbWl0S2V5EikKEGNyZWF0ZV90aW1lc3RhbXAYAyABKANSD2NyZWF0ZVRpbWVzdGFtcBInCg9zdGFydF90aW1lc3RhbXAYBCABKANSDnN0YXJ0VGltZXN0YW1wEiMKDWVuZF90aW1lc3RhbXAYBSABKANSDGVuZFRpbWVzdGFtcBISCgRuYW1lGAYgASgJUgRuYW1lEhoKCGF0dGVtcHRzGAcgASgFUghhdHRlbXB0cxIZCghpc19mbGFreRgIIAEoCFIHaXNGbGFreRIsChJ0aW1lb3V0X2luX21pbnV0ZXMYCSABKAVSEHRpbWVvdXRJbk1pbnV0ZXMSFgoGcmVhc29uGAogASgJUgZyZWFzb24SMwoVcmVxdWlyZWRfY2FwYWJpbGl0aWVzGAsgAygJUhRyZXF1aXJlZENhcGFiaWxpdGllcxIwChRyZXNlcnZlZF9mb3JfYWdlbnRJZBgMIAEoCVIScmVzZXJ2ZWRGb3JBZ2VudElkEh0KCnN0YWdlX25hbWUYDSABKAlSCXN0YWdlTmFtZRIWCgZzdGF0dXMYDiABKAlSBnN0YXR1cxIgCgtidWlsZE51bWJlchgPIAEoBVILYnVpbGROdW1iZXISKAoPYnVpbGROdW1iZXJMaXN0GBAgASgJUg9idWlsZE51bWJlckxpc3QSIAoLYnVpbGRlck5hbWUYESABKAlSC2J1aWxkZXJOYW1lEh4KCmx1Y2lCdWNrZXQYEiABKAlSCmx1Y2lCdWNrZXQSIgoNaXNfdGVzdF9mbGFreRgTIAEoCFILaXNUZXN0Rmxha3k='); diff --git a/dashboard/lib/model/task.pbserver.dart b/dashboard/lib/model/task.pbserver.dart deleted file mode 100644 index 94b9013a0..000000000 --- a/dashboard/lib/model/task.pbserver.dart +++ /dev/null @@ -1,8 +0,0 @@ -/// -// Generated code. Do not modify. -// source: lib/model/task.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 'task.pb.dart'; diff --git a/dashboard/lib/model/task.proto b/dashboard/lib/model/task.proto deleted file mode 100644 index b946ebafe..000000000 --- a/dashboard/lib/model/task.proto +++ /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. - -syntax = "proto2"; - -package dashboard; - -import "lib/model/key.proto"; - -message Task { - optional RootKey key = 1; - optional RootKey commit_key = 2; - optional int64 create_timestamp = 3; - optional int64 start_timestamp = 4; - optional int64 end_timestamp = 5; - optional string name = 6; - optional int32 attempts = 7; - optional bool is_flaky = 8; - optional int32 timeout_in_minutes = 9; - optional string reason = 10; - repeated string required_capabilities = 11; - optional string reserved_for_agentId = 12; - optional string stage_name = 13; - optional string status = 14; - optional int32 buildNumber = 15; - optional string buildNumberList = 16; - optional string builderName = 17; - optional string luciBucket = 18; - optional bool is_test_flaky = 19; -} diff --git a/dashboard/lib/service/appengine_cocoon.dart b/dashboard/lib/service/appengine_cocoon.dart deleted file mode 100644 index 5c2dd51b5..000000000 --- a/dashboard/lib/service/appengine_cocoon.dart +++ /dev/null @@ -1,299 +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:io'; - -import 'package:fixnum/fixnum.dart'; -import 'package:flutter/foundation.dart' show kIsWeb, visibleForTesting; -import 'package:flutter_dashboard/model/branch.pb.dart'; -import 'package:http/http.dart' as http; - -import '../logic/qualified_task.dart'; -import '../model/build_status_response.pb.dart'; -import '../model/commit.pb.dart'; -import '../model/commit_status.pb.dart'; -import '../model/key.pb.dart'; -import '../model/task.pb.dart'; -import 'cocoon.dart'; - -/// CocoonService for interacting with flutter/flutter production build data. -/// -/// This queries API endpoints that are hosted on AppEngine. -class AppEngineCocoonService implements CocoonService { - /// Creates a new [AppEngineCocoonService]. - /// - /// If a [client] is not specified, a new [http.Client] instance is created. - AppEngineCocoonService({http.Client? client}) : _client = client ?? http.Client(); - - /// Branch on flutter/flutter to default requests for. - final String _defaultBranch = 'master'; - - /// The Cocoon API endpoint to query - /// - /// This is the base for all API requests to cocoon - static const String _baseApiUrl = 'flutter-dashboard.appspot.com'; - - final http.Client _client; - - @override - Future>> fetchCommitStatuses({ - CommitStatus? lastCommitStatus, - String? branch, - required String repo, - }) async { - final Map queryParameters = { - if (lastCommitStatus != null) 'lastCommitKey': lastCommitStatus.commit.key.child.name, - 'branch': branch ?? _defaultBranch, - 'repo': repo, - }; - final Uri getStatusUrl = apiEndpoint('/api/public/get-status', queryParameters: queryParameters); - - /// This endpoint returns JSON [List, List] - final http.Response response = await _client.get(getStatusUrl); - - if (response.statusCode != HttpStatus.ok) { - return CocoonResponse>.error('/api/public/get-status returned ${response.statusCode}'); - } - - try { - final Map jsonResponse = jsonDecode(response.body); - return CocoonResponse>.data( - _commitStatusesFromJson(jsonResponse['Statuses']), - ); - } catch (error) { - return CocoonResponse>.error(error.toString()); - } - } - - @override - Future>> fetchRepos() async { - final Uri getReposUrl = apiEndpoint('/api/public/repos'); - - // This endpoint returns a JSON array of strings.1 - final http.Response response = await _client.get(getReposUrl); - - if (response.statusCode != HttpStatus.ok) { - return CocoonResponse>.error('$getReposUrl returned ${response.statusCode}'); - } - - List repos; - try { - repos = List.from(jsonDecode(response.body) as List); - } on FormatException { - return CocoonResponse>.error('$getReposUrl had a malformed response'); - } - return CocoonResponse>.data(repos); - } - - @override - Future> fetchTreeBuildStatus({ - String? branch, - required String repo, - }) async { - final Map queryParameters = { - 'branch': branch ?? _defaultBranch, - 'repo': repo, - }; - final Uri getBuildStatusUrl = apiEndpoint('/api/public/build-status', queryParameters: queryParameters); - - /// This endpoint returns JSON {AnticipatedBuildStatus: [BuildStatus]} - final http.Response response = await _client.get(getBuildStatusUrl); - - if (response.statusCode != HttpStatus.ok) { - return CocoonResponse.error('/api/public/build-status returned ${response.statusCode}'); - } - - BuildStatusResponse protoResponse; - try { - protoResponse = BuildStatusResponse.fromJson(response.body); - } on FormatException { - return const CocoonResponse.error('/api/public/build-status had a malformed response'); - } - return CocoonResponse.data(protoResponse); - } - - @override - Future>> fetchFlutterBranches() async { - final Uri getBranchesUrl = apiEndpoint('/api/public/get-release-branches'); - - /// This endpoint returns JSON {"Branches": List} - final http.Response response = await _client.get(getBranchesUrl); - - if (response.statusCode != HttpStatus.ok) { - return CocoonResponse>.error('/api/public/get-release-branches returned ${response.statusCode}'); - } - - try { - final List jsonResponse = jsonDecode(response.body); - final List branches = []; - for (final Map jsonBranch in jsonResponse) { - branches.add( - Branch() - ..branch = jsonBranch['branch']! - ..channel = jsonBranch['name']!, - ); - } - return CocoonResponse>.data(branches); - } catch (error) { - return CocoonResponse>.error(error.toString()); - } - } - - @override - Future vacuumGitHubCommits(String idToken) async { - final Uri refreshGitHubCommitsUrl = apiEndpoint('/api/vacuum-github-commits'); - final http.Response response = await _client.get( - refreshGitHubCommitsUrl, - headers: { - 'X-Flutter-IdToken': idToken, - }, - ); - return response.statusCode == HttpStatus.ok; - } - - @override - Future> rerunTask(Task task, String? idToken, String repo) async { - if (idToken == null || idToken.isEmpty) { - return const CocoonResponse.error('Sign in to trigger reruns'); - } - - final QualifiedTask qualifiedTask = QualifiedTask.fromTask(task); - assert(qualifiedTask.isLuci); - - /// This endpoint only returns a status code. - final Uri postResetTaskUrl = apiEndpoint('/api/reset-prod-task'); - final http.Response response = await _client.post( - postResetTaskUrl, - headers: { - 'X-Flutter-IdToken': idToken, - }, - body: jsonEncode({ - 'Key': task.key.child.name, - 'Repo': repo, - }), - ); - - if (response.statusCode == HttpStatus.ok) { - return const CocoonResponse.data(true); - } - - return CocoonResponse.error('HTTP Code: ${response.statusCode}, ${response.body}'); - } - - /// Construct the API endpoint based on the priority of using a local endpoint - /// before falling back to the production endpoint. - /// - /// This functions resolves the relative url endpoint to the production endpoint - /// that can be used on web to the production endpoint if running not on web. - /// This is because only on web a Cocoon backend can be running from the same - /// host as this Flutter application, but on mobile we need to ping a separate - /// production endpoint. - /// - /// The urlSuffix begins with a slash, e.g. "/api/public/get-status". - /// - /// [queryParameters] are appended to the url and are url encoded. - @visibleForTesting - Uri apiEndpoint( - String urlSuffix, { - Map? queryParameters, - }) { - if (kIsWeb) { - return Uri.base.replace(path: urlSuffix, queryParameters: queryParameters); - } - return Uri.https(_baseApiUrl, urlSuffix, queryParameters); - } - - List _commitStatusesFromJson(List? jsonCommitStatuses) { - assert(jsonCommitStatuses != null); - // TODO(chillers): Remove adapter code to just use proto fromJson method. https://github.com/flutter/cocoon/issues/441 - - final List statuses = []; - - for (final Map jsonCommitStatus in jsonCommitStatuses!) { - final Map checklist = jsonCommitStatus['Checklist']; - statuses.add( - CommitStatus() - ..commit = _commitFromJson(checklist) - ..branch = _branchFromJson(checklist)! - ..tasks.addAll(_tasksFromStagesJson(jsonCommitStatus['Stages'])), - ); - } - - return statuses; - } - - String? _branchFromJson(Map jsonChecklist) { - final Map checklist = jsonChecklist['Checklist']; - return checklist['Branch'] as String?; - } - - Commit _commitFromJson(Map jsonChecklist) { - final Map checklist = jsonChecklist['Checklist']; - - final Map commit = checklist['Commit']; - final Map author = commit['Author']; - - final Commit result = Commit() - ..key = (RootKey()..child = (Key()..name = jsonChecklist['Key'] as String)) - ..timestamp = Int64() + checklist['CreateTimestamp']! - ..sha = commit['Sha'] as String - ..author = author['Login'] as String - ..authorAvatarUrl = author['avatar_url'] as String - ..repository = checklist['FlutterRepositoryPath'] as String - ..branch = checklist['Branch'] as String; - if (commit['Message'] != null) { - result.message = commit['Message'] as String; - } - return result; - } - - List _tasksFromStagesJson(List json) { - final List tasks = []; - - for (final Map jsonStage in json) { - tasks.addAll(_tasksFromJson(jsonStage['Tasks'])); - } - - return tasks; - } - - List _tasksFromJson(List json) { - final List tasks = []; - - for (final Map jsonTask in json) { - //as Iterable> - tasks.add(_taskFromJson(jsonTask)); - } - - return tasks; - } - - Task _taskFromJson(Map json) { - final Map taskData = json['Task']; - final List? objectRequiredCapabilities = taskData['RequiredCapabilities'] as List?; - - final Task task = Task() - ..key = (RootKey()..child = (Key()..name = json['Key'] as String)) - ..createTimestamp = Int64(taskData['CreateTimestamp'] as int) - ..startTimestamp = Int64(taskData['StartTimestamp'] as int) - ..endTimestamp = Int64(taskData['EndTimestamp'] as int) - ..name = taskData['Name'] as String - ..attempts = taskData['Attempts'] as int - ..isFlaky = taskData['Flaky'] as bool - ..timeoutInMinutes = taskData['TimeoutInMinutes'] as int - ..reason = taskData['Reason'] as String - ..requiredCapabilities.add(objectRequiredCapabilities.toString()) - ..reservedForAgentId = taskData['ReservedForAgentID'] as String - ..stageName = taskData['StageName'] as String - ..status = taskData['Status'] as String - ..isTestFlaky = taskData['TestFlaky'] as bool? ?? false; - - task - ..buildNumberList = taskData['BuildNumberList'] as String? ?? '' - ..builderName = taskData['BuilderName'] as String? ?? '' - ..luciBucket = taskData['LuciBucket'] as String? ?? ''; - return task; - } -} diff --git a/dashboard/lib/service/cocoon.dart b/dashboard/lib/service/cocoon.dart deleted file mode 100644 index 0a40b7db6..000000000 --- a/dashboard/lib/service/cocoon.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 'package:flutter/foundation.dart'; -import 'package:flutter_dashboard/model/branch.pb.dart'; - -import '../model/build_status_response.pb.dart'; -import '../model/commit_status.pb.dart'; -import '../model/task.pb.dart'; -import 'appengine_cocoon.dart'; -import 'dev_cocoon.dart'; - -/// Service class for interacting with flutter/flutter build data. -/// -/// This service exists as a common interface for getting build data from a data source. -abstract class CocoonService { - /// Creates a new [CocoonService] based on if the Flutter app is in production. - /// - /// If `useProductionService` is true, then use the production Cocoon backend - /// running on AppEngine, otherwise use fake data populated from a fake - /// service. Defaults to production data on a release build, and fake data on - /// a debug build. - factory CocoonService({bool useProductionService = kReleaseMode}) { - if (useProductionService) { - return AppEngineCocoonService(); - } - return DevelopmentCocoonService(DateTime.now(), simulateLoadingDelays: true); - } - - /// Gets build information on the most recent commits. - /// - /// If [lastCommitStatus] is given, it will return the next page of - /// [List] after [lastCommitStatus], not including it. - Future>> fetchCommitStatuses({ - CommitStatus? lastCommitStatus, - String? branch, - required String repo, - }); - - /// Gets the current build status of flutter/flutter. - Future> fetchTreeBuildStatus({ - String? branch, - required String repo, - }); - - /// Get the current list of version branches in flutter/flutter. - Future>> fetchFlutterBranches(); - - /// Get the current list of version branches in flutter/flutter. - Future>> fetchRepos(); - - /// Send rerun [Task] command to devicelab. - /// - /// Will not rerun tasks that are outside of devicelab. - - Future> rerunTask(Task task, String? idToken, String repo); - - /// Force update Cocoon to get the latest commits. - Future vacuumGitHubCommits(String idToken); -} - -/// Wrapper class for data this state serves. -/// -/// Holds [data] and possible error information. -@immutable -class CocoonResponse { - const CocoonResponse.data(this.data) : error = null; - const CocoonResponse.error(this.error) : data = null; - - /// The data that gets used from [CocoonService]. - final T? data; - - /// Error information that can be used for debugging. - final String? error; -} - -/// This must be kept up to date with what's in app_dart/lib/src/service/config.dart. -final Map defaultBranches = { - 'cocoon': 'main', - 'engine': 'main', - 'flutter': 'master', - 'packages': 'main', -}; diff --git a/dashboard/lib/service/dev_cocoon.dart b/dashboard/lib/service/dev_cocoon.dart deleted file mode 100644 index 2763391ea..000000000 --- a/dashboard/lib/service/dev_cocoon.dart +++ /dev/null @@ -1,394 +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:math' as math; - -import 'package:fixnum/fixnum.dart'; -import 'package:flutter_dashboard/model/branch.pb.dart'; - -import '../logic/qualified_task.dart'; -import '../model/build_status_response.pb.dart'; -import '../model/commit.pb.dart'; -import '../model/commit_status.pb.dart'; -import '../model/key.pb.dart'; -import '../model/task.pb.dart'; -import 'cocoon.dart'; - -class _PausedCommitStatus { - _PausedCommitStatus(CocoonResponse> status) - : _completer = Completer>>(), - _pausedStatus = status; - - final Completer>> _completer; - CocoonResponse>? _pausedStatus; - - bool get isComplete => _pausedStatus == null; - - Future>> get future => _completer.future; - - void update(CocoonResponse> newStatus) { - assert(_pausedStatus != null); - _pausedStatus = newStatus; - } - - void complete() { - assert(_pausedStatus != null); - _completer.complete(_pausedStatus); - _pausedStatus = null; - } -} - -/// [CocoonService] for local development purposes. -/// -/// This creates fake data that mimicks what production will send. -class DevelopmentCocoonService implements CocoonService { - DevelopmentCocoonService(this.now, {this.simulateLoadingDelays = false}) - : _random = math.Random(now.millisecondsSinceEpoch); - - final math.Random _random; - - final DateTime now; - - final bool simulateLoadingDelays; - - _PausedCommitStatus? _pausedStatus; - bool _paused = false; - bool get paused => _paused; - set paused(bool pause) { - if (_paused == pause) { - return; - } - assert(_paused || _pausedStatus == null || _pausedStatus!.isComplete); - if (_pausedStatus != null && !_pausedStatus!.isComplete) { - _pausedStatus!.complete(); - _pausedStatus = null; - } - _paused = pause; - } - - @override - Future>> fetchCommitStatuses({ - CommitStatus? lastCommitStatus, - String? branch, - required String repo, - }) async { - final CocoonResponse> data = - CocoonResponse>.data(_createFakeCommitStatuses(lastCommitStatus, repo)); - if (_pausedStatus == null || _pausedStatus!.isComplete) { - _pausedStatus = _PausedCommitStatus(data); - } else { - _pausedStatus!.update(data); - } - - if (!_paused) { - if (simulateLoadingDelays) { - final _PausedCommitStatus? delayedStatus = _pausedStatus; - Future.delayed(const Duration(seconds: 2), () { - if (!_paused && !delayedStatus!.isComplete) { - delayedStatus.complete(); - } - }); - } else { - _pausedStatus!.complete(); - } - } - - return _pausedStatus!.future; - } - - static const List _repos = [ - 'flutter', - 'engine', - 'cocoon', - ]; - - @override - Future>> fetchRepos() async { - return const CocoonResponse>.data(_repos); - } - - @override - Future> fetchTreeBuildStatus({ - String? branch, - required String repo, - }) async { - final bool failed = _random.nextBool(); - final BuildStatusResponse response = BuildStatusResponse() - ..buildStatus = failed ? EnumBuildStatus.failure : EnumBuildStatus.success; - if (failed) { - response.failingTasks.addAll(['failed_task_1', 'failed_task_2']); - } - - return CocoonResponse.data(response); - } - - @override - Future>> fetchFlutterBranches() async { - final List fakeBranches = [ - Branch() - ..channel = 'HEAD' - ..branch = 'master', - Branch() - ..channel = 'stable' - ..branch = 'flutter-3.13-candidate.0', - Branch() - ..channel = 'beta' - ..branch = 'flutter-3.15-candidate.5', - Branch() - ..channel = 'dev' - ..branch = 'flutter-3.15-candidate.12', - ]; - return CocoonResponse>.data(fakeBranches); - } - - @override - Future vacuumGitHubCommits(String idToken) async { - return false; - } - - @override - Future> rerunTask(Task task, String? accessToken, String repo) async { - return const CocoonResponse.error( - 'Unable to retry against fake data. Try building the app to use prod data.', - ); - } - - static const int _commitGap = 2 * 60 * 1000; // 2 minutes between commits - - List _createFakeCommitStatuses(CommitStatus? lastCommitStatus, String repo) { - final int baseTimestamp = - lastCommitStatus != null ? (lastCommitStatus.commit.timestamp.toInt()) : now.millisecondsSinceEpoch; - - final List result = []; - for (int index = 0; index < 25; index += 1) { - final int commitTimestamp = baseTimestamp - ((index + 1) * _commitGap); - final math.Random random = math.Random(commitTimestamp); - final Commit commit = _createFakeCommit(commitTimestamp, random, repo, _commits[index]); - final CommitStatus status = CommitStatus() - ..branch = defaultBranches[repo]! - ..commit = commit - ..tasks.addAll(_createFakeTasks(commitTimestamp, commit, random)); - result.add(status); - } - return result; - } - - final List _authors = ['alice', 'bob', 'charlie', 'dobb', 'eli', 'fred']; - final List _messagePrimes = [3, 11, 17, 23, 31, 41, 47, 67, 79]; - final List _words = ['fixes', 'issue', 'crash', 'developer', 'blocker', 'intermittent', 'format']; - final List _commits = [ - '2d22b5e85f986f3fa2cf1bfaf085905c2182c270', - '2fd76f920a38e4384248173d05ee482d5aeaf4c5', - '77238bc7bf35489df03bc00ce2b2231a1afe6b06', - '01d87b7a802e6ea388a066e773b1af3dace44053', - '754039ae0cc524db1052da0f22c9275e32fe4f54', - '1f5e006a398fa5d0e59f78cd5071e2532d2fe438', - 'a798b24044d567df62b8693b179932a8364c8dd8', - '4389a8a3dbe1ed4c6a643641e95d7759f2158d9e', - 'c05e886884ec9adff2f43b87dbcb02e3507d971b', - 'a2acc46447cdfcd6628a897dea27ff64849bfc99', - 'd31d67ffb38fbd09ecf0a11ad5f6fd433cec9c9f', - 'e303d2c71c956c9c1eb7bf81473aac20d756eb75', - '789f6bb335fe31de5d9c6adbab2fc169030a057f', - 'c97f0670658e04f096a3e57b20fa8241306ffcaa', - 'e8e31198861b5f53f04900bdf9a54e8bf6b7d597', - '434a0a7d3c4c3bc633e06de7707ef590c54c20c3', - 'e7fca29b3f0408c7c1726e270a5aba0e28e74090', - 'dd6f94d8573a77506a122899a0592a956ac57bec', - 'cb19ec23d79b9d422f577722e5a14253fdcaea71', - '4cb8c6498aedfd2ff0f89e34eb5da993a77392bd', - 'de892884aa089f22aced4d19a71b6a1d521c8db6', - '214470ceb026525fa225d52dfe7d27db2c4ddf31', - 'e0f4628b4379286c433bab020b9e193fdc437d05', - 'be5975cb0b7fcad7ab2c3122a8df3d541befdeab', - '06f7ab60d4914e00b342f098e1ef3e43e501b469', - 'e1005bea192673e54faa0c769d9f0fb7439a09b4', - 'e22d7f6bf2e1e1969dd963d39ad32c756fb0f20e', - 'f4b4b20bb27cbbd1c42eee7b17ce39c5819dc818', - 'd717ed969b6a477499eb3cf823c78dbe654ca709', - '6a8d6a42b4c9f4b72caf0a3b808e50909686a2b7', - 'afaa9bfa5d26ab419f791d5ca97d602ec52a30a5', - 'f553212d9bfbc6e70c0d6a4ac3fe71208bb77ca1', - 'c35f8b79f4a5103603ceaaa14d1df3857a166fa1', - '46447341838d480966926d0b32771e281af1c885', - '4e3330081d4e0a3e109cf1cfb514072a90b999d7', - '4199ef93c3184c29362520ea5292d854a8728494', - 'a0538938974c60cb9249acf8f0588c3df3c0e4b1', - '49cac0b6c1e0d7d1d04865c328c8cbe5c8e0cda2', - '97e3cc6295515f8292f5f57868506c70446594c3', - 'c94e577ef3b0ab48255633c63f0143d9e3eab6f8', - '17d9af726fb6e4a4a8cc4f1becfc11dc9d4db96c', - '4af6ec81ad538e3da23ee88cba65ed8687a72ea3', - '69cb45166d2f4f61069e1f1a975dab3f48bed832', - 'ae227082570f75b614bb29593911ada5137654ad', - '882b9fa44962df0b80d9a25b2553796bd4eba2ff', - 'e1f5c4c7178c34ce561c493b558df7450995a60c', - '56d7272cd497c73502ec5a09bdf69b4c7ecbfd74', - '36a99b302f7348848ec477ce867a8b78656d9c6c', - '48be7a01565289f44c2c9d4ff1436800c52acf75', - '422cb82e01ee0192256a05217102f45f2f74551d', - 'f8744d10460abae3d75e92336b3bb264bb78cc8c', - '5000f246c8c568547c551b11e4b72acd0179e73c', - '52c09522b288766a42900aa73f77216907d16b23', - '61adb59fc097335b45b23fc884a78362b71a3e9b', - 'b81c3c58e5a6c45bc8728024068291a7e5f19c1a', - 'dec394e5401b62024ed71253e900c25a06eb46f9', - 'fbe142d2cb60fb981521cfe72961642d69db8784', - '24a9c4557e0366945c179433fab8434c2c8ee59f', - 'ccc28f607ae11fe79c39c42d88798c19b1388af9', - 'd4d5ca665e56063fdd37d42d2192c64151915454', - '4273fc07c3e8a36726087becdf331f7d63fc7e66', - '5e0380895cc8e34a6a58f238b7fd33b9e0f027fe', - '146908e48a8a9f92f59fae86a7a4ff28c0ce9109', - '2779a449026423bc0f50a10eead2d9f3720b825c', - 'ff9b954908341360ffb39f5eee2857172a29c0e5', - 'b00b6d146c4f4af1d28260a3f28abd15f0105221', - '4ffd03df44741d73877a0848935eea0d6cbfc1e9', - 'c2c5b73aaae2583cc235319e53200e0e15cda438', - 'b7b370faf46f4c682827fbb3967e1ef117b3b0d1', - 'bc6c9ac81e5909408518568729c2c3a147402928', - 'ba1aa77c5bc5fe545b245f2b1883af3dfeeae6c0', - '97a7060a7e28ab1d13cb2f4ca90be624b417168e', - '3df1da69ffcb4e00cf13c65c9f0497a56413e5e9', - 'ebb13b4ed0cb41814839d4084c064e62526b2ec0', - '70b9d50722307f0f45ceed01d421d6a425e95291', - '3f81ca7b73f21ca9b449c147942fbe4fcd31450d', - '86d490939df232966dc6ba363ebc0baaac364701', - '6ba51f3f61025543d66e59c2c1ae3d1b37d5d8d6', - 'b728942e1c9a9046c1a5c8d8a25a644d0db7160c', - '920819252a398c0e3da9b6017b110ee047c1748e', - 'd7c114d803b1365ea3a975c9600fc8cfd9efb9d4', - '792aa82143bb12e97f396cb2a462ad617dbd22bc', - ]; - - Commit _createFakeCommit(int commitTimestamp, math.Random random, String repo, String commitSha) { - final int author = random.nextInt(_authors.length); - final int message = commitTimestamp % 37 + author; - final int messageInc = _messagePrimes[message % _messagePrimes.length]; - return Commit() - ..key = (RootKey()..child = (Key()..name = '$commitTimestamp')) - ..author = _authors[author] - ..authorAvatarUrl = 'https://avatars2.githubusercontent.com/u/${2148558 + author}?v=4' - ..message = List.generate(6, (int i) => _words[(message + i * messageInc) % _words.length]).join(' ') - ..repository = 'flutter/$repo' - ..sha = commitSha - ..timestamp = Int64(commitTimestamp) - ..branch = 'master'; - } - - static const Map _repoTaskCount = { - 'flutter/cocoon': 3, - 'flutter/flutter': 100, - 'flutter/engine': 20, - }; - - List _createFakeTasks(int commitTimestamp, Commit commit, math.Random random) { - if (_repoTaskCount.containsKey(commit.repository) == false) { - throw Exception('Add ${commit.repository} to _repoTaskCount in DevCocoonService'); - } - return List.generate( - _repoTaskCount[commit.repository]!, - (int i) => _createFakeTask(commitTimestamp, i, StageName.luci, random), - ); - } - - static const List _statuses = [ - 'New', - 'In Progress', - 'Succeeded', - 'Succeeded Flaky', - 'Failed', - 'Underperformed', - 'Underperfomed In Progress', - 'Skipped', - ]; - - static const Map _minAttempts = { - 'New': 0, - 'In Progress': 1, - 'Succeeded': 1, - 'Succeeded Flaky': 1, - 'Failed': 1, - 'Underperformed': 1, - 'Underperfomed In Progress': 1, - 'Skipped': 0, - }; - - static const Map _maxAttempts = { - 'New': 0, - 'In Progress': 1, - 'Succeeded': 1, - 'Succeeded Flaky': 2, - 'Failed': 2, - 'Underperformed': 2, - 'Underperfomed In Progress': 2, - 'Skipped': 0, - }; - - Task _createFakeTask(int commitTimestamp, int index, String stageName, math.Random random) { - final int age = (now.millisecondsSinceEpoch - commitTimestamp) ~/ _commitGap; - assert(age >= 0); - // The [statusesProbability] list is an list of proportional - // weights to give each of the values in _statuses when randomly - // determining the status. So e.g. if one is 150, another 50, and - // the rest 0, then the first has a 75% chance of being picked, - // the second a 25% chance, and the rest a 0% chance. - final List statusesProbability = [ - // bigger = more probable - math.max(index % 2, 20 - age * 2), // blue - math.max(0, 10 - age * 2), // spinny - math.min(10 + age * 2, 100), // green - math.min(1 + age ~/ 3, 30), // yellow - if (index % 15 == 0) // red - 5 - else if (index % 25 == 0) // red - 15 - else - 1, - 1, // orange - 1, // orange spinny - if (index == now.millisecondsSinceEpoch % 20) // white - math.max(0, 1000 - age * 20) - else if (index == now.millisecondsSinceEpoch % 22) - math.max(0, 1000 - age * 10) - else - 0, - ]; - // max is the sum of all the values in statusesProbability. - final int max = statusesProbability.fold(0, (int c, int p) => c + p); - // weightedIndex is the random number in the range 0 <= weightedIndex < max. - int weightedIndex = random.nextInt(max); - // statusIndex is the actual index into _statuses that corresponds - // to the randomly selected weightedIndex. So if - // statusesProbability is 10,20,30 and weightedIndex is 15, then - // the statusIndex will be 1 (corresponding to the second entry, - // the one with weight 20, since lists are zero-indexed). - int statusIndex = 0; - while (weightedIndex > statusesProbability[statusIndex]) { - weightedIndex -= statusesProbability[statusIndex]; - statusIndex += 1; - } - // Finally we get the actual status using statusIndex as an index into _statuses. - final String status = _statuses[statusIndex]; - final int minAttempts = _minAttempts[status]!; - final int maxAttempts = _maxAttempts[status]!; - final int attempts = minAttempts + random.nextInt(maxAttempts - minAttempts + 1); - final Task task = Task() - ..createTimestamp = Int64(commitTimestamp + index) - ..startTimestamp = Int64(commitTimestamp + (index * 1000 * 60)) - ..endTimestamp = Int64(commitTimestamp + (index * 1000 * 60) + (index * 1000 * 60)) - ..name = 'Linux_android $index' - ..builderName = 'Linux_android $index' - ..attempts = attempts - ..isFlaky = index == now.millisecondsSinceEpoch % 13 - ..requiredCapabilities.add('[linux/android]') - ..reservedForAgentId = 'linux1' - ..stageName = stageName - ..status = status - ..isTestFlaky = index == now.millisecondsSinceEpoch % 17; - - return task; - } -} diff --git a/dashboard/lib/service/google_authentication.dart b/dashboard/lib/service/google_authentication.dart deleted file mode 100644 index f0092cded..000000000 --- a/dashboard/lib/service/google_authentication.dart +++ /dev/null @@ -1,99 +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:flutter/foundation.dart'; -import 'package:flutter/services.dart'; -import 'package:google_sign_in/google_sign_in.dart'; - -/// Service class for interacting with Google Sign In authentication for Cocoon backend. -/// -/// Almost all operations with the plugin can throw an exception and should be caught -/// to prevent this service from crashing. -class GoogleSignInService extends ChangeNotifier { - /// Creates a new [GoogleSignIn]. - GoogleSignInService({GoogleSignIn? googleSignIn}) - : _googleSignIn = googleSignIn ?? - GoogleSignIn( - scopes: _googleScopes, - ) { - _googleSignIn.onCurrentUserChanged.listen((GoogleSignInAccount? accountValue) { - // We could decode the idToken here and look at its TTL, but it can be - // revoked (from another website) at any time, so the only reliable way of - // knowing that we're still authenticated is... to monitor the statuses of - // the requests to the server (if they return an error, clear user/log out!) - user = accountValue; - notifyListeners(); - }); - - try { - _googleSignIn.signInSilently(); - } on PlatformException catch (error) { - debugPrint('GoogleSignIn error code: ${error.code}'); - debugPrint(error.message); - } - } - - /// A list of Google API OAuth Scopes this project needs access to. - /// - /// Currently, the project shows just basic user profile information - /// when logged in. - /// - /// See https://developers.google.com/identity/protocols/googlescopes - static const List _googleScopes = []; - - /// The instance of the GoogleSignIn plugin to use. - final GoogleSignIn _googleSignIn; - - /// Whether or not the application has been signed in to. - /// - /// If the plugin fails, default to unauthenticated. - bool get isAuthenticated { - return user != null; - } - - /// The Google Account for the signed in user, null if no user is signed in. - /// - /// Read only object with only access to clear client auth tokens. - GoogleSignInAccount? user; - - /// Authentication token to be sent to Cocoon Backend to verify API calls. - Future get idToken async { - assert(isAuthenticated, 'Ensure user isAuthenticated before requesting an idToken.'); - - final GoogleSignInAuthentication? key = await user?.authentication; - final String? idToken = key?.idToken; - assert(idToken != null && idToken.isNotEmpty); - - return idToken!; - } - - /// Initiate the Google Sign In process. - Future signIn() async { - try { - user = await _googleSignIn.signIn(); - notifyListeners(); - } on PlatformException catch (error) { - debugPrint('GoogleSignIn error code: ${error.code}'); - debugPrint(error.message); - } - } - - Future signOut() async { - try { - user = await _googleSignIn.signOut(); - notifyListeners(); - } on PlatformException catch (error) { - debugPrint('GoogleSignIn error code: ${error.code}'); - debugPrint(error.message); - } - } - - /// Clears the active user from the service, without calling signOut on the plugin. - /// - /// This refreshes the UI of the app, while making it easy for users to re-login. - Future clearUser() async { - user = null; - notifyListeners(); - } -} diff --git a/dashboard/lib/state/build.dart b/dashboard/lib/state/build.dart deleted file mode 100644 index 39d0939ad..000000000 --- a/dashboard/lib/state/build.dart +++ /dev/null @@ -1,437 +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:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_app_icons/flutter_app_icons.dart'; -import 'package:flutter_dashboard/model/branch.pb.dart'; - -import '../logic/brooks.dart'; -import '../model/build_status_response.pb.dart'; -import '../model/commit.pb.dart'; -import '../model/commit_status.pb.dart'; -import '../model/key.pb.dart'; -import '../model/task.pb.dart'; -import '../service/cocoon.dart'; -import '../service/google_authentication.dart'; - -/// State for the Flutter Build Dashboard. -class BuildState extends ChangeNotifier { - BuildState({ - required this.cocoonService, - required this.authService, - }) { - authService.addListener(notifyListeners); - } - - /// Cocoon backend service that retrieves the data needed for this state. - final CocoonService cocoonService; - - /// Authentication service for managing Google Sign In. - GoogleSignInService authService; - - /// Recent branches for flutter related to releases. - List get branches => _branches; - List _branches = [ - Branch() - ..branch = 'master' - ..repository = 'flutter', - ]; - - /// The active flutter branches to show data from. - String get currentBranch => _currentBranch; - String _currentBranch = 'master'; - - /// The current repo from [repos] to show data from. - String get currentRepo => _currentRepo; - String _currentRepo = 'flutter'; - - /// Repos in the Flutter organization this dashboard supports. - List get repos => _repos; - List _repos = ['flutter']; - - /// The current status of the commits loaded. - List get statuses => _statuses; - List _statuses = []; - - /// Whether or not flutter/flutter currently passes tests. - bool? get isTreeBuilding => _isTreeBuilding; - bool? _isTreeBuilding; - - List get failingTasks => _failingTasks; - List _failingTasks = []; - - /// Whether more [List] can be loaded from Cocoon. - /// - /// If [fetchMoreCommitStatuses] returns no data, it is assumed the last - /// [CommitStatus] has been loaded. - bool get moreStatusesExist => _moreStatusesExist; - bool _moreStatusesExist = true; - - /// A [Brook] that reports when errors occur that relate to this [BuildState]. - Brook get errors => _errors; - final ErrorSink _errors = ErrorSink(); - - @visibleForTesting - static const String errorMessageFetchingStatuses = 'An error occurred fetching build statuses from Cocoon'; - - @visibleForTesting - static const String errorMessageFetchingTreeStatus = 'An error occurred fetching tree status from Cocoon'; - - @visibleForTesting - static const String errorMessageRerunTasks = 'An error occurred rerunning tasks from Cocoon'; - - @visibleForTesting - static const String errorMessageFetchingFailingTasks = - 'An error occurred fetching the list of failing tasks from Cocoon'; - - @visibleForTesting - static const String errorMessageFetchingBranches = - 'An error occurred fetching branches from flutter/flutter on Cocoon.'; - - @visibleForTesting - static const String errorMessageFetchingRepos = 'An error occurred fetching repos from flutter/flutter on Cocoon.'; - - @visibleForTesting - static const String errorMessageRefreshGitHubCommits = 'An error occurred refreshing GitHub commits.'; - - /// How often to query the Cocoon backend for the current build state. - @visibleForTesting - final Duration? refreshRate = const Duration(seconds: 30); - - /// Timer that calls [_fetchStatusUpdates] on a set interval. - @visibleForTesting - @protected - Timer? refreshTimer; - - // There's no way to cancel futures in the standard library so instead we just track - // if we've been disposed, and if so, we drop everything on the floor. - bool _active = true; - - @override - void addListener(VoidCallback listener) { - if (!hasListeners) { - _startFetchingStatusUpdates(); - assert(refreshTimer != null); - } - super.addListener(listener); - } - - @override - void removeListener(VoidCallback listener) { - super.removeListener(listener); - if (!hasListeners) { - refreshTimer?.cancel(); - refreshTimer = null; - } - } - - /// Start a fixed interval loop that fetches build state updates based on [refreshRate]. - void _startFetchingStatusUpdates() { - assert(refreshTimer == null); - _fetchBranches(); - _fetchRepos(); - _fetchStatusUpdates(); - refreshTimer = Timer.periodic(refreshRate!, _fetchStatusUpdates); - } - - /// Request the latest [branches] from [CocoonService]. - Future _fetchBranches() async { - final CocoonResponse> response = await cocoonService.fetchFlutterBranches(); - - if (response.error != null) { - _errors.send('$errorMessageFetchingBranches: ${response.error}'); - } else { - _branches = response.data!; - notifyListeners(); - } - } - - /// Request the latest [repos] from [CocoonService]. - Future _fetchRepos() async { - final CocoonResponse> response = await cocoonService.fetchRepos(); - if (response.error != null) { - _errors.send('$errorMessageFetchingRepos: ${response.error}'); - } else { - _repos = response.data!; - notifyListeners(); - } - } - - /// Request the latest [statuses] and [isTreeBuilding] from [CocoonService]. - /// - /// If fetched [statuses] is not on the current branch it will be discarded. - Future _fetchStatusUpdates([Timer? timer]) async { - await Future.wait(>[ - () async { - final String queriedRepoBranch = '$currentRepo/$currentBranch'; - final CocoonResponse> response = - await cocoonService.fetchCommitStatuses(branch: currentBranch, repo: currentRepo); - if (!_active) { - return null; - } - if (response.error != null) { - _errors.send('$errorMessageFetchingStatuses: ${response.error}'); - } else if (queriedRepoBranch != '$currentRepo/$currentBranch') { - // No-op as the dashboard shouldn't update with old data - return; - } else { - _mergeRecentCommitStatusesWithStoredStatuses(response.data!); - notifyListeners(); - } - }(), - () async { - final flutterAppIconsPlugin = FlutterAppIcons(); - final String queriedRepoBranch = '$currentRepo/$currentBranch'; - final CocoonResponse response = await cocoonService.fetchTreeBuildStatus( - branch: currentBranch, - repo: currentRepo, - ); - if (!_active) { - return null; - } - if (response.error != null) { - _errors.send('$errorMessageFetchingTreeStatus: ${response.error}'); - } else if (queriedRepoBranch != '$currentRepo/$currentBranch') { - // No-op as the dashboard shouldn't update with old data - return; - } else { - _isTreeBuilding = response.data!.buildStatus == EnumBuildStatus.success; - _failingTasks = response.data!.failingTasks; - if (_isTreeBuilding == false) { - unawaited(flutterAppIconsPlugin.setIcon(icon: 'favicon-failure.png')); - } else { - unawaited(flutterAppIconsPlugin.setIcon(icon: 'favicon.png')); - } - notifyListeners(); - } - }(), - ]); - } - - /// Update build state to be on [repo] and erase previous data. - void updateCurrentRepoBranch(String repo, String branch) { - if (currentRepo == repo && currentBranch == branch) { - // Do nothing if the repo hasn't changed. - return; - } - _currentRepo = repo; - _currentBranch = branch; - - _moreStatusesExist = true; - _isTreeBuilding = null; - _failingTasks = []; - _statuses = []; - - _fetchStatusUpdates(); - } - - /// Handle merging status updates with the current data in [statuses]. - /// - /// [recentStatuses] is expected to be sorted from newest commit to oldest - /// commit. This is the same order as [statuses]. - /// - /// If the current list of statuses is empty, [recentStatuses] is set - /// to be the current [statuses]. - /// - /// Otherwise, follow this algorithm: - /// 1. Create a new [List] that is from [recentStatuses]. - /// 2. Find where [recentStatuses] does not have [CommitStatus] that - /// [statuses] has. This is called the [lastKnownIndex]. - /// 3. Append the range of [statuses] from ([lastKnownIndex] to the end of - /// statuses) to [recentStatuses]. This is the merged [statuses]. - void _mergeRecentCommitStatusesWithStoredStatuses( - List recentStatuses, - ) { - if (!_statusesMatchCurrentBranch(recentStatuses)) { - // Do not merge statuses if they are not from the current branch. - // Happens in delayed network requests after switching branches. - return; - } - - /// If the current statuses is empty, no merge logic is necessary. - /// This is used on the first call for statuses. - if (_statuses.isEmpty) { - _statuses = recentStatuses; - return; - } - - assert(_statusesInOrder(recentStatuses)); - final List mergedStatuses = List.from(recentStatuses); - - /// Bisect statuses to find the set that doesn't exist in [recentStatuses]. - final CommitStatus lastRecentStatus = recentStatuses.last; - final int lastKnownIndex = _findCommitStatusIndex(_statuses, lastRecentStatus); - - /// If this assertion error occurs, the Cocoon backend needs to be updated - /// to return more commit statuses. This error will only occur if there - /// is a gap between [recentStatuses] and [statuses]. - assert(lastKnownIndex != -1); - - final int firstIndex = lastKnownIndex + 1; - final int lastIndex = _statuses.length; - - /// If the current statuses has the same statuses as [recentStatuses], - /// there will be no subset of remaining statuses. Instead, it will give - /// a list with a null generated [CommitStatus]. Therefore we manually - /// return an empty list. - final List remainingStatuses = (firstIndex < lastIndex) - ? _statuses - .getRange( - firstIndex, - lastIndex, - ) - .toList() - : []; - - mergedStatuses.addAll(remainingStatuses); - - _statuses = mergedStatuses; - assert(_statusesAreUnique(statuses)); - } - - /// Find the index in [statuses] that has [statusToFind] based on the key. - /// Return -1 if it does not exist. - /// - /// The rest of the data in the [CommitStatus] can be different. - int _findCommitStatusIndex( - List statuses, - CommitStatus statusToFind, - ) { - for (int index = 0; index < statuses.length; index += 1) { - final CommitStatus current = _statuses[index]; - if (current.commit.key == statusToFind.commit.key) { - return index; - } - } - return -1; - } - - Future? _moreStatuses; - - /// When the user reaches the end of [statuses], we load more from Cocoon - /// to create an infinite scroll effect. - /// - /// This method is idempotent (calling it when it's already running will - /// just return the same Future without kicking off more work). - Future? fetchMoreCommitStatuses() { - if (_moreStatuses != null) { - return _moreStatuses; - } - _moreStatuses = _fetchMoreCommitStatusesInternal(); - _moreStatuses!.whenComplete(() { - _moreStatuses = null; - }); - return _moreStatuses; - } - - Future _fetchMoreCommitStatusesInternal() async { - assert(_statuses.isNotEmpty); - - final CocoonResponse> response = await cocoonService.fetchCommitStatuses( - lastCommitStatus: _statuses.last, - branch: currentBranch, - repo: currentRepo, - ); - if (!_active) { - return; - } - if (response.error != null) { - _errors.send('$errorMessageFetchingStatuses: ${response.error}'); - return; - } - final List newStatuses = response.data!; - - /// Handle the case where release branches only have a few commits. - if (newStatuses.isEmpty) { - _moreStatusesExist = false; - notifyListeners(); - return; - } - - assert(_statusesInOrder(newStatuses)); - - /// The [List] returned is the statuses that come at the end - /// of our current list and can just be appended. - _statuses.addAll(newStatuses); - notifyListeners(); - - assert(_statusesAreUnique(statuses)); - } - - Future refreshGitHubCommits() async { - if (!authService.isAuthenticated) { - return false; - } - final bool successful = await cocoonService.vacuumGitHubCommits(await authService.idToken); - if (!successful) { - _errors.send(errorMessageRefreshGitHubCommits); - await authService.clearUser(); - } - return successful; - } - - Future rerunTask(Task task) async { - if (!authService.isAuthenticated) { - return false; - } - final CocoonResponse response = await cocoonService.rerunTask(task, await authService.idToken, _currentRepo); - if (response.error != null) { - _errors.send('$errorMessageRerunTasks: ${response.error}'); - await authService.clearUser(); - return false; - } - return true; - } - - /// Assert that [statuses] is ordered from newest commit to oldest. - bool _statusesInOrder(List statuses) { - for (int i = 0; i < statuses.length - 1; i++) { - final Commit current = statuses[i].commit; - final Commit next = statuses[i + 1].commit; - - if (current.timestamp < next.timestamp) { - return false; - } - } - - return true; - } - - /// Assert that there are no duplicate commits in [statuses]. - bool _statusesAreUnique(List statuses) { - final Set uniqueStatuses = {}; - for (int i = 0; i < statuses.length; i += 1) { - final Commit current = statuses[i].commit; - if (uniqueStatuses.contains(current.key)) { - return false; - } - uniqueStatuses.add(current.key); - } - return true; - } - - /// Check if the latest [List] matches the current branch. - /// - /// When switching branches, there is potential for the previous branch data - /// to come in. In that case, the dashboard should ignore that data. - /// - /// Returns true if [List] is data from the current branch. - bool _statusesMatchCurrentBranch(List statuses) { - assert(statuses.isNotEmpty); - - final CommitStatus exampleStatus = statuses.first; - return exampleStatus.branch == _currentBranch; - } - - @override - void dispose() { - authService.removeListener(notifyListeners); - refreshTimer?.cancel(); - _active = false; - super.dispose(); - } -} diff --git a/dashboard/lib/state/index.dart b/dashboard/lib/state/index.dart deleted file mode 100644 index 79e3ebe65..000000000 --- a/dashboard/lib/state/index.dart +++ /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. - -import 'package:flutter/foundation.dart'; - -import '../logic/brooks.dart'; -import '../service/google_authentication.dart'; - -/// State for the index page. -class IndexState extends ChangeNotifier { - /// Creates a new [IndexState]. - IndexState({ - required this.authService, - }) { - authService.addListener(notifyListeners); - } - - /// Authentication service for managing Google Sign In. - final GoogleSignInService authService; - - /// A [Brook] that reports when errors occur that relate to this [IndexState]. - /// - /// Currently no errors are ever reported here. - Brook get errors => _errors; - final ErrorSink _errors = ErrorSink(); - - @override - void dispose() { - authService.removeListener(notifyListeners); - super.dispose(); - } -} diff --git a/dashboard/lib/widgets/app_bar.dart b/dashboard/lib/widgets/app_bar.dart deleted file mode 100644 index bf3dab45a..000000000 --- a/dashboard/lib/widgets/app_bar.dart +++ /dev/null @@ -1,38 +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:flutter/material.dart'; - -import 'user_sign_in.dart'; - -/// Cocoon-specific variant of the [AppBar] widget. -/// -/// The [actions] will always have a [UserSignIn] added. -class CocoonAppBar extends StatelessWidget implements PreferredSizeWidget { - const CocoonAppBar({super.key, this.title, this.actions, this.backgroundColor}); - - final Widget? title; - - final List? actions; - - final Color? backgroundColor; - - @override - Size get preferredSize => const Size.fromHeight(kToolbarHeight); - - @override - Widget build(BuildContext context) { - return AppBar( - title: title, - backgroundColor: backgroundColor, - actions: [ - ...?actions, - const Padding( - padding: EdgeInsets.only(right: 8.0), - child: UserSignIn(), - ), - ], - ); - } -} diff --git a/dashboard/lib/widgets/commit_author_avatar.dart b/dashboard/lib/widgets/commit_author_avatar.dart deleted file mode 100644 index 0091fb247..000000000 --- a/dashboard/lib/widgets/commit_author_avatar.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 'package:flutter/material.dart'; - -import '../model/commit.pb.dart'; -import 'web_image.dart'; - -/// Shows the appropriate avatar for a [Commit]'s author. -/// -/// Tries to use the author's image from GitHub, but failing that, uses a [CircleAvatar] -/// with the author's first name and a color arbitrarily but deterministically generated -/// from the avatar's name. -class CommitAuthorAvatar extends StatelessWidget { - const CommitAuthorAvatar({ - super.key, - this.commit, - }); - - final Commit? commit; - - @override - Widget build(BuildContext context) { - assert(commit!.author.isNotEmpty); - final String authorName = commit!.author; - final String authorInitial = authorName.substring(0, 1).toUpperCase(); - final int authorHash = authorName.hashCode; - final ThemeData theme = Theme.of(context); - - final double hue = (360.0 * authorHash / (1 << 15)) % 360.0; - final double themeValue = HSVColor.fromColor(theme.colorScheme.background).value; - Color authorColor = HSVColor.fromAHSV(1.0, hue, 0.4, themeValue).toColor(); - if (theme.brightness == Brightness.dark) { - authorColor = HSLColor.fromColor(authorColor).withLightness(.65).toColor(); - } - - /// Fallback widget that shows the initial of the commit author. In cases - /// where GitHub is down or slow internet this will be seen. - final Widget avatar = CircleAvatar( - backgroundColor: authorColor, - child: Text( - authorInitial, - style: TextStyle(color: authorColor.computeLuminance() > 0.25 ? Colors.black : Colors.white), - ), - ); - - return WebImage( - imageUrl: commit!.authorAvatarUrl, - placeholder: avatar, - ); - } -} diff --git a/dashboard/lib/widgets/commit_box.dart b/dashboard/lib/widgets/commit_box.dart deleted file mode 100644 index 075ec68b7..000000000 --- a/dashboard/lib/widgets/commit_box.dart +++ /dev/null @@ -1,214 +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:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:url_launcher/url_launcher.dart'; - -import '../model/commit.pb.dart'; -import 'commit_author_avatar.dart'; - -// TODO(ianh): Factor out the logic in task_overlay.dart and use it here as well, -// so that all our popups have the same look and feel and we don't duplicate code. - -/// Displays Git commit information. -/// -/// On click, it will open an [OverlayEntry] with [CommitOverlayContents] -/// to show the information provided. Otherwise, it just shows the avatar -/// for the author of this commit. Clicking outside of the [OverlayEntry] -/// will close it. -class CommitBox extends StatefulWidget { - const CommitBox({ - super.key, - required this.commit, - }); - - /// The commit being shown - final Commit commit; - - @override - CommitBoxState createState() => CommitBoxState(); -} - -class CommitBoxState extends State { - OverlayEntry? _commitOverlay; - - @override - Widget build(BuildContext context) { - return InkWell( - onTap: _handleTap, - child: Padding( - padding: const EdgeInsets.all(4.0), - child: CommitAuthorAvatar(commit: widget.commit), - ), - ); - } - - void _handleTap() { - _commitOverlay = OverlayEntry( - builder: (_) => CommitOverlayContents( - parentContext: context, - commit: widget.commit, - closeCallback: _closeOverlay, - ), - ); - - Overlay.of(context).insert(_commitOverlay!); - } - - void _closeOverlay() => _commitOverlay?.remove(); -} - -/// Displays the information from a Git commit. -/// -/// This is intended to be inserted in an [OverlayEntry] as it requires -/// [closeCallback] that will remove the widget from the tree. -class CommitOverlayContents extends StatelessWidget { - const CommitOverlayContents({ - super.key, - required this.parentContext, - required this.commit, - required this.closeCallback, - }); - - /// The parent context that has the size of the whole screen - final BuildContext parentContext; - - /// The commit data to display in the overlay - final Commit commit; - - /// This callback removes the parent overlay from the widget tree. - /// - /// On a click that is outside the area of the overlay (the rest of the screen), - /// this callback is called closing the overlay. - final void Function() closeCallback; - - @override - Widget build(BuildContext context) { - final ThemeData theme = Theme.of(context); - final RenderBox renderBox = parentContext.findRenderObject() as RenderBox; - final Offset offsetLeft = renderBox.localToGlobal(Offset.zero); - return Stack( - children: [ - // This is the area a user can click (the rest of the screen) to close the overlay. - GestureDetector( - onTap: closeCallback, - child: Container( - width: MediaQuery.of(parentContext).size.width, - height: MediaQuery.of(parentContext).size.height, - // Color must be defined otherwise the container can't be clicked on - color: Colors.transparent, - ), - ), - Positioned( - width: 300, - // Move this overlay to be where the parent is - top: offsetLeft.dy + (renderBox.size.height / 2), - left: offsetLeft.dx + (renderBox.size.width / 2), - child: Card( - child: SafeArea( - minimum: const EdgeInsets.all(16), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - CommitAuthorAvatar(commit: commit), - Expanded( - child: Padding( - padding: const EdgeInsetsDirectional.only(start: 16), - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Align( - alignment: Alignment.centerLeft, - child: AnimatedDefaultTextStyle( - style: theme.textTheme.titleMedium!, - duration: kThemeChangeDuration, - child: Row( - children: [ - Hyperlink( - text: commit.sha.substring(0, 7), - onPressed: _openGithub, - ), - IconButton( - icon: const Icon(Icons.copy), - onPressed: () => unawaited( - Clipboard.setData( - ClipboardData(text: commit.sha), - ), - ), - ), - ], - ), - ), - ), - const SizedBox(height: 8), - Padding( - padding: const EdgeInsets.only(bottom: 4), - child: AnimatedDefaultTextStyle( - style: theme.textTheme.bodyMedium!.copyWith( - color: theme.textTheme.bodySmall!.color, - ), - duration: kThemeChangeDuration, - child: SelectableText(commit.message.split('\n').first), - ), - ), - SelectableText(commit.author), - ], - ), - ), - ), - ], - ), - ), - ), - ), - ], - ); - } - - Future _openGithub() async { - final String githubUrl = 'https://github.com/${commit.repository}/commit/${commit.sha}'; - await launchUrl(Uri.parse(githubUrl)); - } -} - -class Hyperlink extends StatefulWidget { - const Hyperlink({ - super.key, - required this.text, - this.onPressed, - }); - - final String text; - final VoidCallback? onPressed; - - @override - HyperlinkState createState() => HyperlinkState(); -} - -class HyperlinkState extends State { - bool hover = false; - - @override - Widget build(BuildContext context) { - final TextStyle defaultStyle = DefaultTextStyle.of(context).style; - return MouseRegion( - onEnter: (PointerEnterEvent _) => setState(() => hover = true), - onExit: (PointerExitEvent _) => setState(() => hover = false), - cursor: SystemMouseCursors.click, - child: GestureDetector( - onTap: widget.onPressed, - child: Text( - widget.text, - style: defaultStyle.copyWith( - color: const Color(0xff1377c0), - decoration: hover ? TextDecoration.underline : TextDecoration.none, - ), - ), - ), - ); - } -} diff --git a/dashboard/lib/widgets/error_brook_watcher.dart b/dashboard/lib/widgets/error_brook_watcher.dart deleted file mode 100644 index f1e94f156..000000000 --- a/dashboard/lib/widgets/error_brook_watcher.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:flutter/material.dart'; - -import '../logic/brooks.dart'; - -/// Show error messages as snackbars. -/// -/// This widget must be a descendant of a [Scaffold]. -/// -/// The [errors] brook is watched and any messages sent to that brook -/// are displayed as [SnackBar]s on the nearest [Scaffold]. -class ErrorBrookWatcher extends StatefulWidget { - const ErrorBrookWatcher({ - super.key, - this.errors, - this.child, - }); - - final Brook? errors; - - final Widget? child; - - @visibleForTesting - static const Duration errorSnackbarDuration = Duration(seconds: 8); - - @override - State createState() => _ErrorBrookWatcherState(); -} - -class _ErrorBrookWatcherState extends State { - @override - void initState() { - super.initState(); - widget.errors!.addListener(_showErrorSnackbar); - } - - @override - void didUpdateWidget(ErrorBrookWatcher oldWidget) { - super.didUpdateWidget(oldWidget); - if (widget.errors != oldWidget.errors) { - oldWidget.errors!.removeListener(_showErrorSnackbar); - widget.errors!.addListener(_showErrorSnackbar); - } - } - - @override - void dispose() { - widget.errors!.removeListener(_showErrorSnackbar); - super.dispose(); - } - - void _showErrorSnackbar(String error) { - final Row snackbarContent = Row( - children: [ - const Icon(Icons.error), - const SizedBox(width: 10), - Text(error), - ], - ); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: snackbarContent, - backgroundColor: Theme.of(context).colorScheme.error, - duration: ErrorBrookWatcher.errorSnackbarDuration, - ), - ); - } - - @override - Widget build(BuildContext context) { - return widget.child!; - } -} diff --git a/dashboard/lib/widgets/filter_property_sheet.dart b/dashboard/lib/widgets/filter_property_sheet.dart deleted file mode 100644 index 3e2820ad6..000000000 --- a/dashboard/lib/widgets/filter_property_sheet.dart +++ /dev/null @@ -1,330 +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:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - -/// Bridge between the [FilterPropertySheet] and a filter object that has a number of -/// properties by which it filters lists. The [sheetLayout] provides both the properties -/// to be displayed and edited and also a suggestion for their layout. -abstract class FilterPropertySource extends Listenable { - /// The list of properties exposed by the filter for editing. - List get sheetLayout; -} - -/// The base class for all elements in a [FilterPropertySheet]. Most of the nodes will -/// be value properties, but some may be layout nodes. -/// -/// @see [ValueFilterProperty], [FilterPropertyGroup] -abstract class FilterPropertyNode { - /// The descriptive name of the property or layout group as will be displayed - /// in the [FilterPropertySheet]. - String? get label; -} - -/// The abstract base class of all valued properties, useful for both displaying them -/// in and editing them from a [FilterPropertySheet] and methods to make them useful -/// as the actual storage for the properties in a filter object. A filter object then -/// becomes mostly a list of these properties along with methods to combine them into -/// predicates for filtering data in a dashboard or other list. -/// -/// @see [RegExpFilterProperty], [BoolFilterProperty] -abstract class ValueFilterProperty extends ValueListenable with FilterPropertyNode { - ValueFilterProperty({required this.fieldName, this.label}); - - /// The name of the field represented by this property, used to import and export - /// the property values via maps. - final String fieldName; - - @override - final String? label; - - /// The value of the property converted to a [String] useful for importing and - /// exporting the values via maps and JSON files. - String get stringValue; - set stringValue(String newValue); - - /// Whether the property is set to its default value. - bool get isDefault; - - /// Resets this property to its default value; - void reset(); - - List? _listeners; - - @override - void addListener(VoidCallback listener) { - _listeners ??= []; - _listeners!.add(listener); - } - - @override - void removeListener(VoidCallback listener) { - _listeners?.remove(listener); - } - - /// Notify all listeners that the value of the property has changed. - void notifyListeners() { - if (_listeners != null) { - for (final VoidCallback listener in _listeners!) { - listener(); - } - } - } -} - -/// A class used to represent a Regular Expression property in the filter object. -class RegExpFilterProperty extends ValueFilterProperty { - RegExpFilterProperty({required super.fieldName, super.label, String? value, bool caseSensitive = true}) - : _value = value, - _caseSensitive = caseSensitive; - - String? _value; - final bool _caseSensitive; - @override - String? get value => _value; - set value(String? newValue) { - if (newValue == '') { - newValue = null; - } - if (_value != newValue) { - _value = newValue; - _regExp = null; - notifyListeners(); - } - newValue ??= ''; - if (_controller != null && _controller!.text != newValue) { - _controller!.text = newValue; - // The listener callback should nop - } - } - - TextEditingController? _controller; - TextEditingController? get controller { - if (_controller == null) { - _controller = TextEditingController(text: stringValue); - _controller!.addListener(() { - value = _controller!.text; - }); - } - return _controller; - } - - @override - String get stringValue => _value ?? ''; - - @override - set stringValue(String newValue) => value = newValue; - - @override - bool get isDefault => _value == null; - - @override - void reset() => value = null; - - /// The value of this property as a [RegExp] object, useful for matching its pattern - /// against candidate values in the list being filtered. - RegExp? _regExp; - RegExp? get regExp => _regExp ??= _value == null ? null : RegExp(_value!, caseSensitive: _caseSensitive); - set regExp(RegExp? newRegExp) => value = newRegExp == null || newRegExp.pattern == '' ? null : newRegExp.pattern; - - /// True iff the value, interpreted as a regular expression, matches the candidate [String]. - bool matches(String candidate) => regExp?.hasMatch(candidate) ?? true; -} - -/// A class used to represent a boolean property in the filter object. -class BoolFilterProperty extends ValueFilterProperty { - BoolFilterProperty({required super.fieldName, super.label, bool value = true}) - : _value = value, - _defaultValue = value; - - bool? _value; - final bool? _defaultValue; - - @override - bool? get value => _value; - set value(bool? newValue) { - if (_value != newValue) { - _value = newValue; - notifyListeners(); - } - } - - @override - String get stringValue => _value.toString(); - - @override - set stringValue(String newValue) { - if (newValue == 'true' || newValue == 't') { - value = true; - } else if (newValue == 'false' || newValue == 'f') { - value = false; - } else { - throw 'Unrecognized bool value: $newValue'; - } - } - - @override - bool get isDefault => value == _defaultValue; - - @override - void reset() => value = _defaultValue; -} - -/// A class used to enclose a group of other [BoolFilterProperty] properties to be -/// presented in a more compact format in the property sheet. -class BoolFilterPropertyGroup extends FilterPropertyNode { - BoolFilterPropertyGroup({this.label, this.members}); - - @override - final String? label; - - /// The boolean property members of this group. - final List? members; -} - -/// A [Widget] used to display the values of the properties of a filter object and to allow -/// a user to edit those properties. The changes are recorded in new filter objects using the -/// [FilterPropertySource.copyWithMap] method and communicated back to the app live via -/// modifying the value of the [filterNotifier]. -/// -/// If an optional [onClose] callback is supplied, this sheet will include its own close control -/// and notify the creator when it is closed via the callback. Otherwise the creator is -/// responsible for the lifecycle of this sheet. -class FilterPropertySheet extends StatefulWidget { - const FilterPropertySheet(this.propertySource, {this.onClose, super.key}); - - /// The notifier object used to get the initial value of the filter properties and to - /// send back new filter objects with modified values as the user edits the fields. - final FilterPropertySource? propertySource; - - /// The optional callback for when the close field on the sheet is used to close the - /// sheet. This [Widget] will only implement its own close box if this callback is non-null. - final Function()? onClose; - - @override - State createState() => FilterPropertySheetState(); -} - -class FilterPropertySheetState extends State { - @override - void initState() { - super.initState(); - - widget.propertySource!.addListener(_update); - } - - @override - void dispose() { - widget.propertySource!.removeListener(_update); - - super.dispose(); - } - - static const TextStyle _labelStyle = TextStyle( - fontSize: 16.0, - fontWeight: FontWeight.normal, - fontStyle: FontStyle.normal, - decoration: TextDecoration.none, - ); - - void _update() { - setState(() {}); - } - - Widget _pad(Widget child, Alignment alignment) { - return Container( - padding: const EdgeInsets.all(5.0), - alignment: alignment, - child: child, - ); - } - - TableRow _makeRow(String label, Widget editable) { - return TableRow( - children: [ - _pad(Text(label, style: _labelStyle), Alignment.centerRight), - _pad(editable, Alignment.centerLeft), - ], - ); - } - - TableRow _makeTextFilterRow(RegExpFilterProperty property) { - return _makeRow( - property.label!, - TextField( - autofocus: true, - controller: property.controller, - decoration: const InputDecoration( - hintText: '(JavaScript regular expression)', - ), - onChanged: (String value) => property.value = value, - ), - ); - } - - TableRow _makeBoolRow(BoolFilterProperty property) { - return _makeRow( - property.label!, - Checkbox( - value: property.value, - onChanged: (bool? newValue) => property.value = newValue, - ), - ); - } - - Widget _makeLoneCheckbox(BoolFilterProperty property) { - return Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text(property.label!, style: _labelStyle), - Checkbox( - value: property.value, - onChanged: (bool? newValue) => property.value = newValue, - ), - ], - ); - } - - TableRow _makeTableRow(FilterPropertyNode property) { - if (property is RegExpFilterProperty) { - return _makeTextFilterRow(property); - } - if (property is BoolFilterProperty) { - return _makeBoolRow(property); - } - if (property is BoolFilterPropertyGroup) { - return _makeRow( - property.label!, - Wrap( - children: property.members!.map(_makeLoneCheckbox).toList(), - ), - ); - } - throw 'unrecognized FilterProperty: $property'; - } - - @override - Widget build(BuildContext context) { - return Stack( - children: [ - if (widget.onClose != null) - Positioned( - child: TextButton( - onPressed: widget.onClose, - child: const Icon(Icons.close), - ), - ), - Table( - defaultVerticalAlignment: TableCellVerticalAlignment.middle, - columnWidths: const { - 0: IntrinsicColumnWidth(), - 1: FixedColumnWidth(300.0), - }, - children: widget.propertySource!.sheetLayout.map(_makeTableRow).toList(), - ), - ], - ); - } -} diff --git a/dashboard/lib/widgets/header_text.dart b/dashboard/lib/widgets/header_text.dart deleted file mode 100644 index 4cef1ce8f..000000000 --- a/dashboard/lib/widgets/header_text.dart +++ /dev/null @@ -1,22 +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:flutter/material.dart'; - -/// Text header. -/// -/// Displays the given text using font headline4. -class HeaderText extends StatelessWidget { - const HeaderText( - this.text, { - super.key, - }); - - final String text; - - @override - Widget build(BuildContext context) { - return Text(text, style: Theme.of(context).textTheme.headlineMedium, textAlign: TextAlign.center); - } -} diff --git a/dashboard/lib/widgets/lattice.dart b/dashboard/lib/widgets/lattice.dart deleted file mode 100644 index 3996bf5f9..000000000 --- a/dashboard/lib/widgets/lattice.dart +++ /dev/null @@ -1,882 +0,0 @@ -// Copyright 2014 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:math' as math; -import 'dart:math'; - -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; - -import 'task_box.dart'; - -typedef Painter = void Function(Canvas canvas, Rect rect); - -typedef LatticeTapCallback = void Function(Offset? offset); - -/// A cell in a [LatticeScrollView]. -@immutable -class LatticeCell extends _LatticeCell { - const LatticeCell({ - super.painter, - this.builder, - super.onTap, - this.taskName, - }); - - final WidgetBuilder? builder; - - final String? taskName; - - @override - bool get hasChild => builder != null; -} - -/// A bidirectional scrollable view that draws arrays of arrays of [LatticeCell]s. -/// -/// Only the [cells] that are visible are drawn. -/// -/// The cells will be sized according to [cellSize]. -class LatticeScrollView extends StatelessWidget { - const LatticeScrollView({ - super.key, - this.horizontalPhysics, - this.horizontalController, - this.textDirection, - this.verticalPhysics, - this.verticalController, - this.dragStartBehavior = DragStartBehavior.start, - required this.cells, - }); - - final ScrollPhysics? horizontalPhysics; - - final ScrollController? horizontalController; - - final TextDirection? textDirection; - - final ScrollPhysics? verticalPhysics; - - final ScrollController? verticalController; - - final DragStartBehavior dragStartBehavior; - - final List> cells; - - @override - Widget build(BuildContext context) { - final TextDirection textDirection = this.textDirection ?? Directionality.of(context); - return Scrollbar( - controller: horizontalController, - thumbVisibility: true, - child: Scrollable( - dragStartBehavior: dragStartBehavior, - axisDirection: textDirectionToAxisDirection(textDirection), - controller: horizontalController, - physics: horizontalPhysics, - scrollBehavior: _MouseDragScrollBehavior.instance, - viewportBuilder: (BuildContext context, ViewportOffset horizontalOffset) => - NotificationListener( - onNotification: (notification) => - notification.metrics.axisDirection != AxisDirection.right && - notification.metrics.axisDirection != AxisDirection.left, - child: Scrollbar( - thumbVisibility: true, - controller: verticalController, - child: Scrollable( - dragStartBehavior: dragStartBehavior, - axisDirection: AxisDirection.down, - controller: verticalController, - physics: verticalPhysics, - scrollBehavior: _MouseDragScrollBehavior.instance, - viewportBuilder: (BuildContext context, ViewportOffset verticalOffset) => _LatticeBody( - textDirection: textDirection, - horizontalOffset: horizontalOffset, - verticalOffset: verticalOffset, - cells: cells, - cellSize: Size.square(TaskBox.of(context)), - ), - ), - ), - ), - ), - ); - } -} - -/// Used to mark classes that would be made public if the rendering object side -/// of this contraption is ever made public. -const Object _public = Object(); - -@_public -class _LatticeBody extends RenderObjectWidget { - const _LatticeBody({ - required this.textDirection, - required this.horizontalOffset, - required this.verticalOffset, - required this.cells, - required this.cellSize, - }); - - final TextDirection textDirection; - final ViewportOffset horizontalOffset; - final ViewportOffset verticalOffset; - final List> cells; - final Size cellSize; - - @override - _RenderLatticeBody createRenderObject(BuildContext context) { - return _RenderLatticeBody( - textDirection: textDirection, - horizontalOffset: horizontalOffset, - verticalOffset: verticalOffset, - cells: cells, - cellSize: cellSize, - delegate: context as _LatticeBodyElement, - ); - } - - @override - void updateRenderObject(BuildContext context, _RenderLatticeBody renderObject) { - renderObject - ..textDirection = textDirection - ..horizontalOffset = horizontalOffset - ..verticalOffset = verticalOffset - ..cells = cells - ..cellSize = cellSize - ..delegate = context as _LatticeBodyElement; - } - - @override - RenderObjectElement createElement() => _LatticeBodyElement(this); -} - -@_public -class _LatticeBodyElement extends RenderObjectElement implements _LatticeDelegate { - _LatticeBodyElement(_LatticeBody super.widget); - - @override - _LatticeBody get widget => super.widget as _LatticeBody; - - @override - _RenderLatticeBody get renderObject => super.renderObject as _RenderLatticeBody; - - // This element uses _Coordinate objects as slots. - - Map _newChildrenByKey = {}; - Map? _oldChildrenByKey; - Map<_Coordinate, Element?> _newChildrenByCoordinate = <_Coordinate, Element?>{}; - Map<_Coordinate, Element?>? _oldChildrenByCoordinate; - - @override - void beginLayout() { - _oldChildrenByKey = _newChildrenByKey; - _newChildrenByKey = {}; - _oldChildrenByCoordinate = _newChildrenByCoordinate; - _newChildrenByCoordinate = <_Coordinate, Element?>{}; - } - - @override - RenderBox? updateLatticeChild(_Coordinate coordinate, LatticeCell cell, RenderBox? oldChild) { - Widget? newWidget; - Element? newElement; - owner!.buildScope(this, () { - try { - newWidget = cell.builder!(this); - debugWidgetBuilderValue(widget, newWidget); - } catch (exception, stack) { - newWidget = ErrorWidget.builder( - _debugReportException( - FlutterErrorDetails( - context: ErrorDescription('building cell $coordinate for $widget'), - exception: exception, - stack: stack, - library: 'Flutter Dashboard', - informationCollector: () sync* { - yield DiagnosticsDebugCreator(DebugCreator(this)); - }, - ), - ), - ); - } - Element? oldElement; - if (newWidget!.key != null) { - oldElement = _oldChildrenByKey![newWidget!.key]; - if (oldElement != null) { - _oldChildrenByKey![newWidget!.key] = null; // null indicates it exists but is not in the grid - _oldChildrenByCoordinate!.remove(oldElement.slot as _Coordinate?); - } - } else { - oldElement = _oldChildrenByCoordinate![coordinate]; - if (oldElement != null && oldElement.widget.key != null) { - oldElement = null; - } - _oldChildrenByCoordinate!.remove(coordinate); - } - try { - newElement = updateChild(oldElement, newWidget, coordinate); - } catch (e, stack) { - newWidget = ErrorWidget.builder( - _debugReportException( - FlutterErrorDetails( - context: ErrorDescription('building widget $newWidget at cell $coordinate for $widget'), - exception: e, - stack: stack, - library: 'Flutter Dashboard', - informationCollector: () sync* { - yield DiagnosticsDebugCreator(DebugCreator(this)); - }, - ), - ), - ); - newElement = updateChild(null, newWidget, slot); - } - }); - assert(newElement!.slot == coordinate); - if (newWidget!.key != null) { - _newChildrenByKey[newWidget!.key] = newElement; - } - _newChildrenByCoordinate[coordinate] = newElement; - return newElement!.renderObject as RenderBox?; - } - - @override - void endLayout() { - for (final Element? oldChild in _oldChildrenByCoordinate!.values) { - if (oldChild!.widget.key == null) { - updateChild(oldChild, null, null); - } - } - for (final Element? oldChild in _oldChildrenByKey!.values) { - updateChild(oldChild, null, null); - } - _oldChildrenByKey = null; - _oldChildrenByCoordinate = null; - } - - @override - void forgetChild(Element child) { - if (child.widget.key != null) { - _newChildrenByKey.remove(child.widget.key); - } - _newChildrenByCoordinate.remove(child.slot as _Coordinate?); - super.forgetChild(child); - } - - @override - void insertRenderObjectChild(RenderObject child, _Coordinate? slot) { - renderObject.placeChild(null, slot, null, child as RenderBox); - } - - @override - void moveRenderObjectChild(RenderObject child, _Coordinate? oldSlot, _Coordinate? newSlot) { - renderObject.placeChild(oldSlot, newSlot, child as RenderBox?, child as RenderBox); - } - - @override - void removeRenderObjectChild(RenderObject child, _Coordinate? slot) { - renderObject.removeChild(slot, child as RenderBox); - } - - @override - void visitChildren(ElementVisitor visitor) { - (_newChildrenByCoordinate.values.whereType().toList()..sort(_compareChildren)).forEach(visitor); - } - - int _compareChildren(Element a, Element b) { - final _Coordinate aSlot = a.slot as _Coordinate; - final _Coordinate bSlot = b.slot as _Coordinate; - return aSlot.compareTo(bSlot); - } - - @override - List debugDescribeChildren() { - final List children = _newChildrenByCoordinate.values.whereType().toList() - ..sort(_compareChildren); - return children.map((Element? child) { - return child!.toDiagnosticsNode(name: child.slot != null ? '${child.slot}' : '(lost)'); - }).toList(); - } -} - -@immutable -@_public -class _Coordinate implements Comparable<_Coordinate> { - const _Coordinate(this.x, this.y); - - final int x; - - final int y; - - @override - int compareTo(_Coordinate other) { - if (y == other.y) { - return x - other.x; - } - return y - other.y; - } - - @override - bool operator ==(Object other) { - if (other.runtimeType != runtimeType) { - return false; - } - return other is _Coordinate && other.x == x && other.y == y; - } - - @override - int get hashCode => Object.hash(x, y); - - @override - String toString() => '($x,$y)'; - - Offset asOffset(Size cellSize) => Offset(x.toDouble() * cellSize.width, y.toDouble() * cellSize.height); -} - -@_public -class _LatticeParentData extends ParentData { - _Coordinate? coordinate; -} - -@immutable -@_public -class _LatticeCell { - const _LatticeCell({ - this.painter, - this.onTap, - }); - - static const _LatticeCell empty = _LatticeCell(); - - final Painter? painter; - - final LatticeTapCallback? onTap; - - @protected - bool get hasChild => false; -} - -@_public -abstract class _LatticeDelegate { - const _LatticeDelegate(); - void beginLayout(); - RenderBox? updateLatticeChild(_Coordinate coordinate, covariant _LatticeCell cell, RenderBox? oldChild); - void endLayout(); -} - -@_public -class _RenderLatticeBody extends RenderBox { - _RenderLatticeBody({ - required TextDirection textDirection, - required ViewportOffset horizontalOffset, - required ViewportOffset verticalOffset, - required List> cells, - required Size cellSize, - required _LatticeDelegate delegate, - }) : assert(!cellSize.isEmpty), - _textDirection = textDirection, - _horizontalOffset = horizontalOffset, - _verticalOffset = verticalOffset, - _cells = cells, - _cellSize = cellSize, - _delegate = delegate { - _handleOffsetChange(); - _recomputeCellDimensions(); - } - - TextDirection get textDirection => _textDirection; - TextDirection _textDirection; - set textDirection(TextDirection value) { - if (value == _textDirection) { - return; - } - _textDirection = value; - markNeedsPaint(); - } - - ViewportOffset get horizontalOffset => _horizontalOffset; - ViewportOffset _horizontalOffset; - set horizontalOffset(ViewportOffset value) { - if (value == _horizontalOffset) { - return; - } - if (attached) { - _horizontalOffset.removeListener(_handleOffsetChange); - } - _horizontalOffset = value; - if (attached) { - _horizontalOffset.addListener(_handleOffsetChange); - } - _handleOffsetChange(); - } - - ViewportOffset get verticalOffset => _verticalOffset; - ViewportOffset _verticalOffset; - set verticalOffset(ViewportOffset value) { - if (value == _verticalOffset) { - return; - } - if (attached) { - _verticalOffset.removeListener(_handleOffsetChange); - } - _verticalOffset = value; - if (attached) { - _verticalOffset.addListener(_handleOffsetChange); - } - _handleOffsetChange(); - } - - int _cellWidthCount = 0, _cellHeightCount = 0; - - List> get cells => _cells; - List> _cells; - set cells(List> value) { - if (value == _cells) { - return; - } - _cells = value; - markNeedsLayout(); - _recomputeCellDimensions(); - } - - void _recomputeCellDimensions() { - _cellWidthCount = cells.fold(0, (int current, List<_LatticeCell> row) => math.max(current, row.length)); - _cellHeightCount = cells.length; - _handleOffsetChange(); - } - - Size get cellSize => _cellSize; - Size _cellSize; - set cellSize(Size value) { - assert(!value.isEmpty); - if (value == _cellSize) { - return; - } - _cellSize = value; - markNeedsLayout(); - } - - _LatticeDelegate get delegate => _delegate; - _LatticeDelegate _delegate; - set delegate(_LatticeDelegate value) { - if (value == _delegate) { - return; - } - _delegate = value; - markNeedsLayout(); - } - - // TODO(ianh): rather than store and paint the children directly in - // this render object, dynamically create _RenderLatticeTiles that - // handle cacheStride x cacheStride sections of the grid. This would - // give us more efficient scrolling since we would not need to - // update them. We would need to make sure to mark them all as - // needing layout when the list of widgets changed. - // - // Currently, we have to repaint everything when we scroll because - // we have no way to cache the paint in a layer. - - _LatticeCell? _getCellFor(_Coordinate coordinate) { - if (coordinate.y < 0 || coordinate.x < 0) { - return null; - } - if (coordinate.y >= cells.length) { - return null; - } - if (coordinate.x >= cells[coordinate.y].length) { - return null; - } - return cells[coordinate.y][coordinate.x]; - } - - bool _hasTapHandler(_Coordinate coordinate) { - return _getCellFor(coordinate)?.onTap != null; - } - - final Map<_Coordinate?, RenderBox> _childrenByCoordinate = <_Coordinate?, RenderBox>{}; - - void placeChild(_Coordinate? oldCoordinate, _Coordinate? newCoordinate, RenderBox? oldChild, RenderBox newChild) { - if (oldChild == newChild) { - return; - } - if (oldChild != null) { - final _LatticeParentData oldChildParentData = oldChild.parentData as _LatticeParentData; - oldChildParentData.coordinate = null; - } - if (oldCoordinate != null) { - _childrenByCoordinate.remove(oldCoordinate); - } - _childrenByCoordinate[newCoordinate] = newChild; - if (newChild.parent != this) { - adoptChild(newChild); - } - final _LatticeParentData newChildParentData = newChild.parentData as _LatticeParentData; - newChildParentData.coordinate = newCoordinate; - } - - void removeChild(_Coordinate? coordinate, RenderBox child) { - if (coordinate != null) { - _childrenByCoordinate.remove(coordinate); - } - dropChild(child); - } - - @override - void setupParentData(RenderObject child) { - if (child.parentData is! ParentData) { - child.parentData = _LatticeParentData(); - } - } - - TapGestureRecognizer? _tap; - - @override - void attach(PipelineOwner owner) { - super.attach(owner); - _horizontalOffset.addListener(_handleOffsetChange); - _verticalOffset.addListener(_handleOffsetChange); - _tap = TapGestureRecognizer(debugOwner: this) - ..onTapDown = _handleTapDown - ..onTapUp = _handleTapUp; - for (final RenderBox child in _childrenByCoordinate.values) { - child.attach(owner); - } - } - - @override - void detach() { - super.detach(); - _horizontalOffset.removeListener(_handleOffsetChange); - _verticalOffset.removeListener(_handleOffsetChange); - _tap?.dispose(); - for (final RenderBox child in _childrenByCoordinate.values) { - child.detach(); - } - } - - @override - void dispose() { - super.dispose(); - _clipLabelColumnHandle.layer = null; - _clipLabelRowHandle.layer = null; - _clipDataHandle.layer = null; - } - - @override - void redepthChildren() { - _childrenByCoordinate.values.forEach(redepthChild); - } - - @override - void visitChildren(RenderObjectVisitor visitor) { - _childrenByCoordinate.values.forEach(visitor); - } - - @override - bool get isRepaintBoundary => true; - - @override - double computeMinIntrinsicWidth(double? height) { - return _cellWidthCount * cellSize.width; - } - - @override - double computeMaxIntrinsicWidth(double height) { - return computeMinIntrinsicWidth(height); - } - - @override - double computeMinIntrinsicHeight(double? width) { - return _cellHeightCount * cellSize.height; - } - - @override - double computeMaxIntrinsicHeight(double width) { - return computeMinIntrinsicHeight(width); - } - - @override - bool get sizedByParent => true; - - @override - void performResize() { - size = Size( - constraints.hasBoundedWidth ? constraints.maxWidth : constraints.constrainWidth(computeMinIntrinsicWidth(null)), - constraints.hasBoundedHeight - ? constraints.maxHeight - : constraints.constrainHeight(computeMinIntrinsicHeight(null)), - ); - horizontalOffset.applyViewportDimension(size.width); - verticalOffset.applyViewportDimension(size.height); - _handleOffsetChange(duringLayout: true); - } - - Offset? _scrollOffset; - int? _firstX, _firstY, _lastX, _lastY; - - void _handleOffsetChange({bool duringLayout = false}) { - if (!hasSize) { - assert(_scrollOffset == null); - return; - } - final Offset scrollOffset = Offset(horizontalOffset.pixels, verticalOffset.pixels); - final int firstX = scrollOffset.dx ~/ cellSize.width; - final int lastX = ((scrollOffset.dx + size.width) / cellSize.width).ceil() - 1; - final int firstY = scrollOffset.dy ~/ cellSize.height; - final int lastY = math.min(((scrollOffset.dy + size.height) / cellSize.height).ceil(), _cellHeightCount) - 1; - if (scrollOffset != _scrollOffset) { - _scrollOffset = scrollOffset; - markNeedsPaint(); - } - if (firstX != _firstX || lastX != _lastX || firstY != _firstY || lastY != _lastY) { - _firstX = firstX; - _lastX = lastX; - _firstY = firstY; - _lastY = lastY; - if (!duringLayout) { - markNeedsLayout(); - } - } - } - - @override - void performLayout() { - assert(_scrollOffset != null); - final BoxConstraints childConstraints = BoxConstraints.tight(cellSize); - invokeLayoutCallback((BoxConstraints constraints) { - delegate.beginLayout(); - }); - for (int y = 0; y < _cellHeightCount; y += 1) { - for (int x = 0; x < _cellWidthCount; x += 1) { - final _Coordinate here = _Coordinate(x, y); - final bool visible = (x == 0 || x >= _firstX!) && x <= _lastX! && (y == 0 || y >= _firstY!) && y <= _lastY!; - assert(y < cells.length); - final _LatticeCell cell = x < cells[y].length ? cells[y][x] : _LatticeCell.empty; - if (visible && cell.hasChild) { - RenderBox? child; - invokeLayoutCallback((BoxConstraints constraints) { - child = delegate.updateLatticeChild(here, cell, _childrenByCoordinate[here]); - }); - assert(child != null); - assert(child!.parent == this); - assert(_childrenByCoordinate[here] == child); - child!.layout(childConstraints); - } - } - } - invokeLayoutCallback((BoxConstraints constraints) { - delegate.endLayout(); - }); - horizontalOffset.applyContentDimensions( - 0.0, - math.max(0.0, computeMinIntrinsicWidth(null) - size.width), - ); - verticalOffset.applyContentDimensions( - 0.0, - math.max(0.0, computeMinIntrinsicHeight(null) - size.height), - ); - } - - final LayerHandle _clipLabelRowHandle = LayerHandle(); - final LayerHandle _clipLabelColumnHandle = LayerHandle(); - final LayerHandle _clipDataHandle = LayerHandle(); - - void _paintCell(PaintingContext context, Offset offset, int x, int y) { - final _Coordinate here = _Coordinate(x, y); - assert(y < cells.length); - final _LatticeCell cell = x < cells[y].length ? cells[y][x] : _LatticeCell.empty; - final Offset topLeft = _coordinateToOffset(here)! + offset; - final Painter? painter = cell.painter; - final RenderBox? child = cell.hasChild ? _childrenByCoordinate[here] : null; - assert(child == _childrenByCoordinate[here]); - assert(cell.hasChild == (child != null)); - if (painter != null) { - painter(context.canvas, topLeft & cellSize); - } - if (child != null) { - context.paintChild(child, topLeft); - } - } - - @override - void paint(PaintingContext context, Offset offset) { - assert(needsCompositing); - final Offset dataOffset = Offset(cellSize.width, cellSize.height); - final Size dataSize = size - dataOffset as Size; - if (dataSize.isEmpty) { - return; - } - _clipLabelColumnHandle.layer = context.pushClipRect( - needsCompositing, - offset, - Rect.fromLTWH(0, dataOffset.dy, cellSize.width, dataSize.height), - (PaintingContext context, Offset offset) { - for (int y = max(1, _firstY!); y <= _lastY!; y += 1) { - _paintCell(context, offset, 0, y); - } - }, - oldLayer: _clipLabelColumnHandle.layer, - ); - _clipLabelRowHandle.layer = context.pushClipRect( - needsCompositing, - offset, - Rect.fromLTWH(dataOffset.dx, 0, dataSize.width, cellSize.height), - (PaintingContext context, Offset offset) { - for (int x = max(1, _firstX!); x <= _lastX!; x += 1) { - _paintCell(context, offset, x, 0); - } - }, - oldLayer: _clipLabelRowHandle.layer, - ); - _clipDataHandle.layer = context.pushClipRect( - needsCompositing, - offset, - dataOffset & dataSize, - (PaintingContext context, Offset offset) { - for (int y = _firstY! + 1; y <= _lastY!; y += 1) { - for (int x = _firstX! + 1; x <= _lastX!; x += 1) { - _paintCell(context, offset, x, y); - } - } - }, - oldLayer: _clipDataHandle.layer, - ); - } - - @override - void applyPaintTransform(RenderBox child, Matrix4 transform) { - final _LatticeParentData childParentData = child.parentData as _LatticeParentData; - final Offset offset = _coordinateToOffset(childParentData.coordinate!)!; - transform.translate(offset.dx, offset.dy); - } - - @override - Rect describeApproximatePaintClip(RenderObject child) => Offset.zero & size; - - @override - void showOnScreen({ - RenderObject? descendant, - Rect? rect, - Duration duration = Duration.zero, - Curve curve = Curves.ease, - }) { - if (descendant != null) { - // TODO(ianh): Implement this. Not having this implemented means - // accessibility scrolling won't work for this viewport. - // - // The implementation should honor allowImplicitScrolling on - // horizontalOffset and verticalOffset, descendant and rect, and - // duration and curve. (If duration is Duration.zero, use jumpTo - // on the offsets, otherwise use animateTo.) - } - super.showOnScreen( - rect: rect, - duration: duration, - curve: curve, - ); - } - - _Coordinate? _offsetToCoordinate(Offset? position) { - late Offset absolute; - switch (textDirection) { - case TextDirection.rtl: - absolute = Offset(position!.dx - _scrollOffset!.dx, position.dy + _scrollOffset!.dy); - break; - case TextDirection.ltr: - absolute = position! + _scrollOffset!; - break; - } - switch (textDirection) { - case TextDirection.rtl: - return _Coordinate( - position.dx + cellSize.width > size.width ? 0 : (size.width - absolute.dx) ~/ cellSize.width, - position.dy < cellSize.height ? 0 : absolute.dy ~/ cellSize.height, - ); - case TextDirection.ltr: - return _Coordinate( - position.dx < cellSize.width ? 0 : absolute.dx ~/ cellSize.width, - position.dy < cellSize.height ? 0 : absolute.dy ~/ cellSize.height, - ); - } - } - - Offset? _coordinateToOffset(_Coordinate coordinate) { - final Offset adjustedScroll = Offset( - coordinate.x == 0 ? 0 : _scrollOffset!.dx, - coordinate.y == 0 ? 0 : _scrollOffset!.dy, - ); - switch (textDirection) { - case TextDirection.rtl: - return Offset( - size.width - (coordinate.x * cellSize.width) - cellSize.width + adjustedScroll.dx, - coordinate.y * cellSize.height - adjustedScroll.dy, - ); - case TextDirection.ltr: - return Offset( - coordinate.x * cellSize.width, - coordinate.y * cellSize.height, - ) - - adjustedScroll; - } - } - - @override - bool hitTestChildren(BoxHitTestResult result, {Offset? position}) { - final _Coordinate? coordinate = _offsetToCoordinate(position); - final RenderBox? child = _childrenByCoordinate[coordinate]; - return child != null && - result.addWithPaintOffset( - offset: _coordinateToOffset(coordinate!), - position: position!, - hitTest: (BoxHitTestResult result, Offset transformed) { - return child.hitTest(result, position: transformed); - }, - ); - } - - @override - bool hitTestSelf(Offset position) => true; - - @override - void handleEvent(PointerEvent event, BoxHitTestEntry entry) { - assert(debugHandleEvent(event, entry)); - final _Coordinate? coordinate = _offsetToCoordinate(event.localPosition); - if (event is PointerDownEvent && _hasTapHandler(coordinate!)) { - _tap?.addPointer(event); - } - } - - _Coordinate? _lastTapDown; - - void _handleTapDown(TapDownDetails details) { - _lastTapDown = _offsetToCoordinate(details.localPosition); - } - - void _handleTapUp(TapUpDetails details) { - final _Coordinate? lastTapUp = _offsetToCoordinate(details.localPosition); - if (_lastTapDown == lastTapUp && _hasTapHandler(lastTapUp!)) { - _getCellFor(_lastTapDown!)!.onTap!(_coordinateToOffset(lastTapUp)); - } - } - - @override - Rect describeSemanticsClip(RenderObject? child) => (Offset.zero & size).inflate(cellSize.longestSide); -} - -FlutterErrorDetails _debugReportException(FlutterErrorDetails details) { - FlutterError.reportError(details); - return details; -} - -/// A [MaterialScrollBehavior] that supports mouse dragging. -class _MouseDragScrollBehavior extends MaterialScrollBehavior { - static _MouseDragScrollBehavior? _instance; - static _MouseDragScrollBehavior get instance => _instance ??= _MouseDragScrollBehavior(); - - @override - Set get dragDevices => { - PointerDeviceKind.touch, - PointerDeviceKind.mouse, - }; -} diff --git a/dashboard/lib/widgets/luci_task_attempt_summary.dart b/dashboard/lib/widgets/luci_task_attempt_summary.dart deleted file mode 100644 index 956550876..000000000 --- a/dashboard/lib/widgets/luci_task_attempt_summary.dart +++ /dev/null @@ -1,56 +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:flutter/material.dart'; -import 'package:url_launcher/url_launcher.dart'; - -import '../model/task.pb.dart'; - -/// Show information regarding each attempt for a luci Task. -/// -/// Currently shows a button that links to each individual log -/// for a Task. -class LuciTaskAttemptSummary extends StatelessWidget { - const LuciTaskAttemptSummary({ - super.key, - required this.task, - }); - - /// The task to show information from. - final Task task; - - @visibleForTesting - static const String luciProdLogBase = 'https://ci.chromium.org/p/flutter/builders'; - - @visibleForTesting - static const String dartInternalLogBase = 'https://ci.chromium.org/p/dart-internal/builders'; - - @override - Widget build(BuildContext context) { - final List buildNumberList = task.buildNumberList.isEmpty ? [] : task.buildNumberList.split(','); - return ListBody( - children: List.generate(buildNumberList.length, (int i) { - return ElevatedButton( - child: Text('OPEN LOG FOR BUILD #${buildNumberList[i]}'), - onPressed: () { - if (task.stageName == 'dart-internal') { - launchUrl(_dartInternalLogUrl(task.builderName, buildNumberList[i])); - } else { - launchUrl(_luciProdLogUrl(task.builderName, buildNumberList[i])); - } - }, - ); - }), - ); - } - - Uri _luciProdLogUrl(String builderName, String buildNumber) { - final String pool = task.isFlaky ? 'staging' : 'prod'; - return Uri.parse('$luciProdLogBase/$pool/$builderName/$buildNumber'); - } - - Uri _dartInternalLogUrl(String builderName, String buildNumber) { - return Uri.parse('$dartInternalLogBase/flutter/$builderName/$buildNumber'); - } -} diff --git a/dashboard/lib/widgets/now.dart b/dashboard/lib/widgets/now.dart deleted file mode 100644 index cf54f6272..000000000 --- a/dashboard/lib/widgets/now.dart +++ /dev/null @@ -1,81 +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:flutter/widgets.dart'; - -/// An inherited widget that reports the current time and -/// ticks once per second. -class Now extends InheritedNotifier> { - /// For production. - Now({ - super.key, - required super.child, - }) : super( - notifier: _Clock(), - ); - - /// For tests. - Now.fixed({ - super.key, - required DateTime dateTime, - required super.child, - }) : super( - notifier: ValueNotifier(dateTime), - ); - - static DateTime? of(BuildContext context) { - final Now now = context.dependOnInheritedWidgetOfExactType()!; - return now.notifier!.value; - } -} - -class _Clock extends ValueNotifier { - _Clock() : super(null); - - Timer? _timer; - - @override - void addListener(VoidCallback listener) { - if (!hasListeners) { - assert(_timer == null); - value = DateTime.now(); - _scheduleTick(); - } - super.addListener(listener); - } - - @override - void removeListener(VoidCallback listener) { - super.removeListener(listener); - if (!hasListeners && _timer != null) { - _timer!.cancel(); - _timer = null; - value = null; - } - } - - void _tick() { - value = DateTime.now(); - _scheduleTick(); - notifyListeners(); - } - - void _scheduleTick() { - // To make the application appear responsive, we try to tick at the start of each second - // (as opposed to just anywhere within a second, each second). To do that, each tick, we - // set up a new timer to fire just as the time on the device reaches a new second, right - // when the milliseconds component of the time is zero. - // - // To compute the time until the next second, we take the current time, ignore all parts - // except the milliseconds, and subtract that from one second. (We have to take care and - // never wait for zero milliseconds; if the milliseconds part is zero, then we must wait - // a full second.) - // - // By scheduling a new tick each time, we also ensure that we skip past any seconds that - // we were too busy to service without increasing the load on the device. - _timer = Timer(Duration(milliseconds: 1000 - (value!.millisecondsSinceEpoch % 1000)), _tick); - } -} diff --git a/dashboard/lib/widgets/progress_button.dart b/dashboard/lib/widgets/progress_button.dart deleted file mode 100644 index c968ed267..000000000 --- a/dashboard/lib/widgets/progress_button.dart +++ /dev/null @@ -1,82 +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:flutter/material.dart'; - -typedef AsyncVoidCallback = Future Function(); - -/// An [ElevatedButton] whose [onPressed] returns a [Future], and which -/// overlays a progress indicator when the button is pressed until the -/// future completes. -/// -/// Technically this violates the Material design guidelines six ways -/// to Sunday but... -class ProgressButton extends StatefulWidget { - const ProgressButton({ - super.key, - this.child, - this.onPressed, - }); - - final Widget? child; - - final AsyncVoidCallback? onPressed; - - @override - State createState() => _ProgressButtonState(); -} - -class _ProgressButtonState extends State { - bool _busy = false; - - void _handlePressed() { - if (_busy) { - return; - } - setState(() { - _busy = true; - }); - widget.onPressed!().whenComplete(() { - if (mounted) { - setState(() { - _busy = false; - }); - } - }); - } - - static const Widget _progressIndicator = Padding( - padding: EdgeInsets.all(12.0), - child: Center( - child: AspectRatio( - aspectRatio: 1.0, - child: CircularProgressIndicator(), - ), - ), - ); - - @override - Widget build(BuildContext context) { - return Stack( - children: [ - ElevatedButton( - onPressed: _busy // dartfmt will soon require this new formatting - ? null - : widget.onPressed != null // dartfmt will soon require this new formatting - ? _handlePressed - : null, - child: widget.child, - ), - if (_busy) - const Positioned( - top: 0.0, - left: 0.0, - right: 0.0, - bottom: 0.0, - child: _progressIndicator, - ), - ], - ); - } -} diff --git a/dashboard/lib/widgets/sign_in_button/sign_in_button.dart b/dashboard/lib/widgets/sign_in_button/sign_in_button.dart deleted file mode 100644 index df255b8d9..000000000 --- a/dashboard/lib/widgets/sign_in_button/sign_in_button.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 'sign_in_button_native.dart' if (dart.library.js_util) 'sign_in_button_web.dart'; diff --git a/dashboard/lib/widgets/sign_in_button/sign_in_button_native.dart b/dashboard/lib/widgets/sign_in_button/sign_in_button_native.dart deleted file mode 100644 index db749f8c8..000000000 --- a/dashboard/lib/widgets/sign_in_button/sign_in_button_native.dart +++ /dev/null @@ -1,25 +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:flutter/material.dart'; -import 'package:provider/provider.dart'; - -import '../../service/google_authentication.dart'; - -/// Widget that users can click to initiate the Sign In process. -class SignInButton extends StatelessWidget { - const SignInButton({super.key}); - - @override - Widget build(BuildContext context) { - final GoogleSignInService authService = Provider.of(context); - final Color textButtonForeground = Theme.of(context).brightness == Brightness.dark ? Colors.white : Colors.black87; - - return TextButton( - style: TextButton.styleFrom(foregroundColor: textButtonForeground), - onPressed: authService.signIn, - child: const Text('SIGN IN'), - ); - } -} diff --git a/dashboard/lib/widgets/sign_in_button/sign_in_button_web.dart b/dashboard/lib/widgets/sign_in_button/sign_in_button_web.dart deleted file mode 100644 index 20971b586..000000000 --- a/dashboard/lib/widgets/sign_in_button/sign_in_button_web.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:flutter/material.dart'; - -import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; -import 'package:google_sign_in_web/google_sign_in_web.dart'; - -/// Widget that users can click to initiate the Sign In process. -class SignInButton extends StatelessWidget { - const SignInButton({super.key}); - - @override - Widget build(BuildContext context) { - return (GoogleSignInPlatform.instance as GoogleSignInPlugin).renderButton(); - } -} diff --git a/dashboard/lib/widgets/state_provider.dart b/dashboard/lib/widgets/state_provider.dart deleted file mode 100644 index ad4c4928b..000000000 --- a/dashboard/lib/widgets/state_provider.dart +++ /dev/null @@ -1,51 +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:flutter/widgets.dart'; -import 'package:provider/provider.dart'; - -import '../service/google_authentication.dart'; -import '../state/build.dart'; -import '../state/index.dart'; - -class StateProvider extends StatelessWidget { - const StateProvider({ - super.key, - this.signInService, - this.indexState, - this.buildState, - this.child, - }); - - final GoogleSignInService? signInService; - - final IndexState? indexState; - - final BuildState? buildState; - - final Widget? child; - - @override - Widget build(BuildContext context) { - return MultiProvider( - providers: >[ - ValueProvider(value: signInService), - ValueProvider(value: indexState), - ValueProvider(value: buildState), - ], - child: child, - ); - } -} - -/// Similar to Provider.value but doesn't complain when -/// the value is a Listenable. -class ValueProvider extends InheritedProvider { - ValueProvider({ - super.key, - required super.value, - super.updateShouldNotify, - super.child, - }) : super.value(); -} diff --git a/dashboard/lib/widgets/task_box.dart b/dashboard/lib/widgets/task_box.dart deleted file mode 100644 index 19dd62e23..000000000 --- a/dashboard/lib/widgets/task_box.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:flutter/foundation.dart'; -import 'package:flutter/material.dart'; - -typedef ShowSnackBarCallback = ScaffoldFeatureController Function(SnackBar snackBar); - -class TaskBox extends StatelessWidget { - const TaskBox({super.key, this.cellSize, required this.child}); - - final Widget child; - final double? cellSize; - - static const double _kDefaultCellSize = 20; - static const double _kWebCellSize = 36; - - /// Status messages that map to [TaskStatus] enums. - // TODO(chillers): Remove these and use TaskStatus enum when available. https://github.com/flutter/cocoon/issues/441 - static const String statusCancelled = 'Cancelled'; - static const String statusFailed = 'Failed'; - static const String statusNew = 'New'; - static const String statusSkipped = 'Skipped'; - static const String statusSucceeded = 'Succeeded'; - static const String statusInfraFailure = 'Infra Failure'; - static const String statusInProgress = 'In Progress'; - - /// A lookup table to define the background color for this TaskBox. - /// - /// The status messages are based on the messages the backend sends. - /// - /// These colors should map to the MILO color scheme. - static const Map statusColor = { - statusCancelled: Colors.lightBlue, - statusFailed: Colors.red, - statusNew: Colors.grey, - statusSkipped: Colors.transparent, - statusSucceeded: Colors.green, - statusInfraFailure: Colors.purple, - statusInProgress: Colors.yellow, - }; - - /// Returns the cell size of the nearest task box, or null if there is no - /// nearest task box. - static double? maybeOf(BuildContext context) { - final _TaskBox? box = context.dependOnInheritedWidgetOfExactType<_TaskBox>(); - return box?.size; - } - - /// Returns the cell size of the nearest task box. - static double of(BuildContext context) { - return maybeOf(context) ?? _kDefaultCellSize; - } - - @override - Widget build(BuildContext context) { - final double size = cellSize ?? (kIsWeb ? _kWebCellSize : _kDefaultCellSize); - return _TaskBox( - size: TaskBox.maybeOf(context) ?? size, - child: child, - ); - } -} - -class _TaskBox extends InheritedWidget { - const _TaskBox({ - required this.size, - required super.child, - }); - - final double size; - - @override - bool updateShouldNotify(covariant _TaskBox oldWidget) => size != oldWidget.size; -} diff --git a/dashboard/lib/widgets/task_grid.dart b/dashboard/lib/widgets/task_grid.dart deleted file mode 100644 index fe20c07fe..000000000 --- a/dashboard/lib/widgets/task_grid.dart +++ /dev/null @@ -1,386 +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:math'; - -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; - -import '../logic/qualified_task.dart'; -import '../logic/task_grid_filter.dart'; -import '../model/commit.pb.dart'; -import '../model/commit_status.pb.dart'; -import '../model/task.pb.dart'; -import '../state/build.dart'; -import 'commit_box.dart'; -import 'lattice.dart'; -import 'task_box.dart'; -import 'task_icon.dart'; -import 'task_overlay.dart'; - -/// Container that manages the layout and data handling for [TaskGrid]. -/// -/// If there's no data for [TaskGrid], it shows [CircularProgressIndicator]. -class TaskGridContainer extends StatelessWidget { - const TaskGridContainer({super.key, this.filter, this.useAnimatedLoading = false}); - - /// A notifier to hold a [TaskGridFilter] object to control the visibility of various - /// rows and columns of the task grid. This filter may be updated dynamically through - /// this notifier from elsewhere if the user starts editing the filter parameters in - /// the settings dialog. - final TaskGridFilter? filter; - - final bool useAnimatedLoading; - - @visibleForTesting - static const String errorFetchCommitStatus = 'An error occurred fetching commit statuses'; - @visibleForTesting - static const String errorFetchTreeStatus = 'An error occurred fetching tree build status'; - @visibleForTesting - static const Duration errorSnackbarDuration = Duration(seconds: 8); - - @override - Widget build(BuildContext context) { - final BuildState buildState = Provider.of(context); - return AnimatedBuilder( - animation: buildState, - builder: (BuildContext context, Widget? child) { - final List commitStatuses = buildState.statuses; - - // Assume if there is no data that it is loading. - if (commitStatuses.isEmpty) { - return const Center( - child: CircularProgressIndicator(), - ); - } - - return TaskBox( - child: TaskGrid( - buildState: buildState, - commitStatuses: commitStatuses, - filter: filter, - useAnimatedLoading: useAnimatedLoading, - ), - ); - }, - ); - } -} - -/// Display results from flutter/flutter repository's continuous integration. -/// -/// Results are displayed in a matrix format. Rows are commits and columns -/// are the results from tasks. -class TaskGrid extends StatefulWidget { - const TaskGrid({ - super.key, - // TODO(ianh): We really shouldn't take both of these, since buildState exposes status as well; - // it's asking for trouble because the tests can (and do) describe a mutually inconsistent state. - required this.buildState, - required this.commitStatuses, - this.filter, - this.useAnimatedLoading = false, - }); - - /// The build status data to display in the grid. - final List commitStatuses; - - /// Reference to the build state to perform actions on [TaskMatrix], like rerunning tasks. - final BuildState buildState; - - final bool useAnimatedLoading; - - /// A [TaskGridFilter] object to control the visibility of various rows and columns of - /// the task grid. This filter may be updated dynamically from elsewhere if the user - /// starts editing the filter parameters in the settings dialog. - final TaskGridFilter? filter; - - @override - State createState() => _TaskGridState(); -} - -/// Look up table for task status weights in the grid. -/// -/// Weights should be in the range [0, 1.0] otherwise too much emphasis is placed on the first N rows, where N is the -/// largest integer weight. -const Map _statusScores = { - 'Failed - Rerun': 1.0, - 'Failed': 0.7, - 'Infra Failure - Rerun': 0.69, - 'Infra Failure': 0.68, - 'Failed - Flaky': 0.67, - 'Infra Failure - Flaky': 0.65, - 'In Progress - Flaky': 0.64, - 'New - Flaky': 0.63, - 'Succeeded - Flaky': 0.61, - 'New - Rerun': 0.5, - 'In Progress - Rerun': 0.4, - 'Unknown': 0.2, - 'In Progress': 0.1, - 'New': 0.1, - 'Succeeded': 0.01, - 'Skipped': 0.0, -}; - -class _TaskGridState extends State { - // TODO(ianh): Cache the lattice cells. Right now we are regenerating the entire - // lattice matrix each time the task grid has to update, regardless of whether - // we've received new data or not. - - ScrollController? verticalController; - ScrollController? horizontalController; - - @override - void initState() { - super.initState(); - verticalController ??= ScrollController(); - horizontalController ??= ScrollController(); - widget.filter?.addListener(() { - setState(() {}); - }); - } - - @override - void dispose() { - verticalController?.dispose(); - horizontalController?.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return LatticeScrollView( - // TODO(ianh): Provide some vertical scroll physics that disable - // the clamping in the vertical direction, so that you can keep - // scrolling past the end instead of hitting a wall every time - // we load. - // TODO(ianh): Trigger the loading from the scroll offset, - // rather than the current hack of loading during build. - cells: _processCommitStatuses(widget), - verticalController: verticalController, - horizontalController: horizontalController, - ); - } - - /// This is the logic for turning the raw data from the [BuildState] object, a list of - /// [CommitStatus] objects, into the data that describes the rendering as used by the - /// [LatticeScrollView], a list of lists of [LatticeCell]s. - /// - /// The process is as follows: - /// - /// 1. We create `rows`, a list of [_Row] objects which are used to temporarily - /// represent each row in the data, where a row basically represents a [Commit]. - /// - /// These are derived from the `commitStatuses` directly -- each [CommitStatus] is one - /// row, representing one [Commit] and all its [Task]s. - /// - /// 2. We walk the `commitStatuses` again, examining each [Task] of each [CommitStatus], - /// - /// For the first 25 rows, we compute a score for each task, one commit at a time, so - /// that we'll be able to sort the tasks later. The score is based on [_statusScores] - /// (the map defined above). Each row is weighted in the score proportional to how - /// far from the first row it is, so the first row has a weight of 1.0, the second a - /// weight of 1/2, the third a weight of 1/3, etc. - /// - /// Then, we update the `rows` list to contain a [LatticeCell] for this task on this - /// commit. The color of the square is derived from [_painterFor], the builder, if - /// any, is derived from [_builderFor], and the tap handler from [_tapHandlerFor]. - /// - /// 3. We create a list that represents all the tasks we've seen so far, sorted by - /// their score (tie-breaking on task names). - /// - /// 4. Finally, we generate the output, by putting together all the data collected in - /// the second step, walking the tasks in the order determined in the third step. - // - // TODO(ianh): Find a way to save the majority of the work done each time we build the - // matrix. If you've scrolled down several thousand rows, you don't want to have to - // rebuild the entire matrix each time you load another 25 rows. - List> _processCommitStatuses(TaskGrid taskGrid) { - TaskGridFilter? filter = taskGrid.filter; - filter ??= TaskGridFilter(); - // 1: PREPARE ROWS - final List filteredStatuses = - taskGrid.commitStatuses.where((CommitStatus commitStatus) => filter!.matchesCommit(commitStatus)).toList(); - final List<_Row> rows = - filteredStatuses.map<_Row>((CommitStatus commitStatus) => _Row(commitStatus.commit)).toList(); - // 2: WALK ALL TASKS - final Map scores = {}; - final Map taskLookupMap = {}; - - int commitCount = 0; - for (final CommitStatus status in filteredStatuses) { - commitCount += 1; - for (final Task task in status.tasks) { - final QualifiedTask qualifiedTask = QualifiedTask.fromTask(task); - if (!filter.matchesTask(qualifiedTask)) { - continue; - } - taskLookupMap[qualifiedTask] = task; - if (commitCount <= 25) { - String weightStatus = task.status; - if (task.isFlaky || task.isTestFlaky) { - // Flaky tasks should be shown after failures and reruns as they take up infra capacity. - weightStatus += ' - Flaky'; - } else if (task.attempts > 1) { - // Reruns take up extra infra capacity and should be prioritized. - weightStatus += ' - Rerun'; - } - // Make the score relative to how long ago it was run. - final double score = _statusScores.containsKey(weightStatus) - ? _statusScores[weightStatus]! / commitCount - : _statusScores['Unknown']! / commitCount; - scores.update( - qualifiedTask, - (double value) => value += score, - ifAbsent: () => score, - ); - } else { - // In case we have a task that doesn't exist in the first 25 rows, - // we still push the task into the table of scores. Otherwise, we - // won't know how to sort the task later. - scores.putIfAbsent( - qualifiedTask, - () => 0.0, - ); - } - rows[commitCount - 1].cells[qualifiedTask] = LatticeCell( - painter: _painterFor(task), - builder: _builderFor(task), - onTap: _tapHandlerFor(status.commit, task), - ); - } - } - // 3: SORT - final List tasks = scores.keys.toList() - ..sort((QualifiedTask a, QualifiedTask b) { - final int scoreComparison = scores[b]!.compareTo(scores[a]!); - if (scoreComparison != 0) { - return scoreComparison; - } - // If the scores are identical, break ties on the name of the task. - // We do that because otherwise the sort order isn't stable. - if (a.stage != b.stage) { - return a.stage!.compareTo(b.stage!); - } - return a.task!.compareTo(b.task!); - }); - - // 4: GENERATE RESULTING LIST OF LISTS - return >[ - [ - const LatticeCell(), - ...tasks.map( - (QualifiedTask task) => - LatticeCell(builder: (BuildContext context) => TaskIcon(qualifiedTask: task), taskName: task.stage), - ), - ], - ...rows.map>( - (_Row row) => [ - LatticeCell( - builder: (BuildContext context) => CommitBox(commit: row.commit), - ), - ...tasks.map((QualifiedTask task) => row.cells[task] ?? const LatticeCell()), - ], - ), - if (widget.buildState.moreStatusesExist) _generateLoadingRow(tasks.length), - ]; - } - - Painter _painterFor(Task task) { - final Paint backgroundPaint = Paint()..color = Theme.of(context).canvasColor; - final Paint paint = Paint() - ..color = TaskBox.statusColor.containsKey(task.status) ? TaskBox.statusColor[task.status]! : Colors.black; - if (task.isFlaky) { - paint.style = PaintingStyle.stroke; - paint.strokeWidth = 2.0; - return (Canvas canvas, Rect rect) { - canvas.drawRect(rect.deflate(6.0), paint); - }; - } - return (Canvas canvas, Rect rect) { - canvas.drawRect(rect.deflate(2.0), paint); - if (task.attempts > 1 || task.isTestFlaky) { - canvas.drawCircle(rect.center, (rect.shortestSide / 2.0) - 6.0, backgroundPaint); - } - }; - } - - WidgetBuilder? _builderFor(Task task) { - if (task.attempts > 1 || task.isTestFlaky) { - return (BuildContext context) { - return Padding( - padding: const EdgeInsets.all(4.0), - child: Icon(Icons.priority_high, size: TaskBox.of(context) * 0.4), - ); - }; - } - return null; - } - - static final List _loadingMessage = - 'LOADING...'.runes.map((int codepoint) => String.fromCharCode(codepoint)).toList(); - - List _generateLoadingRow(int length) { - return [ - LatticeCell( - builder: (BuildContext context) { - return FittedBox( - fit: BoxFit.contain, - child: Padding( - padding: const EdgeInsets.all(12), - child: widget.useAnimatedLoading - ? const RepaintBoundary(child: CircularProgressIndicator()) - : const Icon(Icons.refresh), - ), - ); - }, - ), - for (int index = 0; index < max(length, _loadingMessage.length); index++) - LatticeCell( - builder: (BuildContext context) { - widget.buildState.fetchMoreCommitStatuses(); // This is safe to call many times. - return Text( - _loadingMessage[index % _loadingMessage.length], - style: TextStyle( - fontSize: TaskBox.of(context) * 0.9, - fontWeight: FontWeight.w900, - ), - textAlign: TextAlign.center, - ); - }, - ), - ]; - } - - OverlayEntry? _taskOverlay; - - LatticeTapCallback _tapHandlerFor(Commit commit, Task task) { - return (Offset? localPosition) { - _taskOverlay?.remove(); - _taskOverlay = OverlayEntry( - builder: (BuildContext context) => TaskOverlayEntry( - position: (this.context.findRenderObject() as RenderBox) - .localToGlobal(localPosition!, ancestor: Overlay.of(context).context.findRenderObject()), - task: task, - showSnackBarCallback: ScaffoldMessenger.of(context).showSnackBar, - closeCallback: _closeOverlay, - buildState: widget.buildState, - commit: commit, - ), - ); - Overlay.of(context).insert(_taskOverlay!); - }; - } - - void _closeOverlay() { - _taskOverlay!.remove(); - _taskOverlay = null; - } -} - -class _Row { - _Row(this.commit); - final Commit commit; - final Map cells = {}; -} diff --git a/dashboard/lib/widgets/task_icon.dart b/dashboard/lib/widgets/task_icon.dart deleted file mode 100644 index ecf64dda5..000000000 --- a/dashboard/lib/widgets/task_icon.dart +++ /dev/null @@ -1,133 +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:flutter/material.dart'; -import 'package:url_launcher/url_launcher.dart'; - -import '../logic/qualified_task.dart'; - -import 'task_box.dart'; - -/// Header icon for all [Task]s that map to the same [Task.stageName] -/// and [Task.name]. -/// -/// Intended to be used in the first row of [TaskGrid]. Shows an -/// icon based on [qualifiedTask.stage], defaulting to [Icons.help] if -/// it can't be mapped. On tap, shows the task. -class TaskIcon extends StatelessWidget { - const TaskIcon({ - super.key, - required this.qualifiedTask, - }); - - /// [Task] to get information from. - final QualifiedTask qualifiedTask; - - /// A lookup table for matching [stageName] to [Image]. - /// - /// [stageName] is based on the backend. - Widget stageIconForBrightness(Brightness brightness) { - // Pass this as the `color` for Image's that are black and white icons. - // This only works in the CanvasKit implementation currently, not in DOM. If - // this needs to run in the DOM implementation, it will need to include - // different assets. - final Color? blendFilter = brightness == Brightness.dark ? Colors.white : null; - - if (qualifiedTask.isGoogleTest) { - return Image.asset( - 'assets/googleLogo.png', - color: blendFilter, - ); - } - - if (qualifiedTask.task == null || !(qualifiedTask.isLuci || qualifiedTask.isDartInternal)) { - return Icon( - Icons.help, - color: blendFilter, - ); - } - - final String matchedName = qualifiedTask.task!.toLowerCase(); - final bool isWebTest = matchedName.contains('_web') || matchedName.contains('web_'); - final bool isToolTest = matchedName.contains('_tool') || matchedName.contains('tool_'); - - if (matchedName.contains('_fuchsia')) { - return Padding( - padding: const EdgeInsets.all(2.0), - child: Image.asset( - 'assets/fuchsia.png', - color: blendFilter, - ), - ); - } else if (isWebTest && !isToolTest) { - return Padding( - padding: const EdgeInsets.all(2.0), - child: Image.asset( - 'assets/chromium.png', - color: blendFilter, - ), - ); - } else if (matchedName.contains('_android')) { - return Icon( - Icons.android, - color: blendFilter, - ); - } else if (matchedName.startsWith('linux')) { - return Padding( - padding: const EdgeInsets.all(2.0), - child: Image.asset( - 'assets/linux.png', - color: blendFilter, - ), - ); - } else if (matchedName.startsWith('mac')) { - if (matchedName.contains('_ios')) { - return Icon( - Icons.phone_iphone, - color: blendFilter, - ); - } else { - return Padding( - padding: const EdgeInsets.all(2.0), - child: Image.asset( - 'assets/apple.png', - color: blendFilter, - ), - ); - } - } else if (matchedName.startsWith('win')) { - return Image.asset( - 'assets/windows.png', - color: blendFilter, - ); - } - - return Icon( - Icons.help, - color: blendFilter, - ); - } - - @override - Widget build(BuildContext context) { - final Brightness brightness = Theme.of(context).brightness; - final Widget icon = stageIconForBrightness(brightness); - - return IconTheme.merge( - data: IconThemeData(size: TaskBox.of(context) - 5), - child: InkWell( - onTap: () { - launchUrl(Uri.parse(qualifiedTask.sourceConfigurationUrl)); - }, - child: Tooltip( - message: '${qualifiedTask.task} (${qualifiedTask.stage})', - child: Align( - alignment: Alignment.bottomCenter, - child: icon, - ), - ), - ), - ); - } -} diff --git a/dashboard/lib/widgets/task_overlay.dart b/dashboard/lib/widgets/task_overlay.dart deleted file mode 100644 index 8ba29193c..000000000 --- a/dashboard/lib/widgets/task_overlay.dart +++ /dev/null @@ -1,328 +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:math' as math; - -import 'package:flutter/material.dart'; - -import '../logic/qualified_task.dart'; -import '../model/commit.pb.dart'; -import '../model/task.pb.dart'; -import '../state/build.dart'; -import 'luci_task_attempt_summary.dart'; -import 'now.dart'; -import 'progress_button.dart'; -import 'task_box.dart'; - -class TaskOverlayEntryPositionDelegate extends SingleChildLayoutDelegate { - TaskOverlayEntryPositionDelegate(this.target, {required this.cellSize}); - - final double cellSize; - - /// The offset of the target the tooltip is positioned near in the global - /// coordinate system. - final Offset target; - - static Offset positionDependentBox({ - required Size size, - required Size childSize, - required double cellSize, - required Offset target, - }) { - const double margin = 10.0; - final double verticalOffset = cellSize * .9; - - // VERTICAL DIRECTION - final bool fitsBelow = target.dy + verticalOffset + childSize.height <= size.height - margin; - double y; - if (fitsBelow) { - y = math.min(target.dy + verticalOffset, size.height - margin); - } else { - y = math.max(target.dy - childSize.height, margin); - } - // HORIZONTAL DIRECTION - double x; - // The whole size isn't big enough, just center it. - if (size.width - margin * 2.0 < childSize.width) { - x = (size.width - childSize.width) / 2.0; - } else { - final double normalizedTargetX = (target.dx).clamp(margin, size.width - margin); - final double edge = normalizedTargetX + childSize.width; - // Position the box as close to the left edge of the full size - // without going over the margin. - if (edge > size.width) { - x = size.width - margin - childSize.width; - } else { - x = normalizedTargetX; - } - } - return Offset(x, y); - } - - @override - Offset getPositionForChild(Size size, Size childSize) { - return positionDependentBox( - size: size, - childSize: childSize, - cellSize: cellSize, - target: target, - ); - } - - @override - bool shouldRelayout(TaskOverlayEntryPositionDelegate oldDelegate) { - return oldDelegate.target != target; - } -} - -/// Displays the information from [Task] and allows interacting with a [Task]. -/// -/// This is intended to be inserted in an [OverlayEntry] as it requires -/// [closeCallback] that will remove the widget from the tree. -class TaskOverlayEntry extends StatelessWidget { - const TaskOverlayEntry({ - super.key, - required this.position, - required this.task, - required this.showSnackBarCallback, - required this.closeCallback, - required this.buildState, - required this.commit, - }); - - /// The global position where to show the task overlay. - final Offset position; - - /// The [Task] to display in the overlay. - final Task task; - - final ShowSnackBarCallback showSnackBarCallback; - - /// This callback removes the parent overlay from the widget tree. - /// - /// On a click that is outside the area of the overlay (the rest of the screen), - /// this callback is called closing the overlay. - final VoidCallback closeCallback; - - /// A reference to the [BuildState] for performing operations on this [Task]. - final BuildState buildState; - - /// [Commit] for tasks to show log. - final Commit commit; - - @override - Widget build(BuildContext context) { - // If this is ever positioned not at the top-left of the viewport, then - // we should make sure to convert the position to the Overlay's coordinate - // space otherwise it'll be misaligned. - return Stack( - children: [ - // This is a focus container to emphasize the cell that this - // [Overlay] is currently showing information from. - Positioned( - top: position.dy, - left: position.dx, - width: TaskBox.of(context), - height: TaskBox.of(context), - child: Container( - decoration: BoxDecoration( - border: Border.all(color: Colors.black, width: 4.0), - color: Colors.white70, - ), - ), - ), - // This is the area a user can click (the rest of the screen) to close the overlay. - GestureDetector( - onTap: closeCallback, - behavior: HitTestBehavior.opaque, - child: const SizedBox.expand(), - ), - Positioned( - // Move this overlay to be where the parent is - child: CustomSingleChildLayout( - delegate: TaskOverlayEntryPositionDelegate(position, cellSize: TaskBox.of(context)), - child: TaskOverlayContents( - showSnackBarCallback: showSnackBarCallback, - buildState: buildState, - task: task, - commit: commit, - closeCallback: closeCallback, - ), - ), - ), - ], - ); - } -} - -/// Displays the information from [Task] and allows interacting with a [Task]. -/// -/// This is intended to be inserted in [TaskOverlayEntry]. -/// -/// Offers the functionality of opening the log for this [Task] and rerunning -/// this [Task] through the build system. -class TaskOverlayContents extends StatelessWidget { - const TaskOverlayContents({ - super.key, - required this.showSnackBarCallback, - required this.buildState, - required this.task, - required this.closeCallback, - this.commit, - }); - - final ShowSnackBarCallback showSnackBarCallback; - - /// A reference to the [BuildState] for performing operations on this [Task]. - final BuildState buildState; - - /// The [Task] to display in the overlay - final Task task; - - /// [Commit] for tasks to show log. - final Commit? commit; - - /// This callback removes the parent overlay from the widget tree. - /// - /// This is used in this scope to close this overlay on redirection to view - /// the agent for this task in the agent dashboard. - final void Function() closeCallback; - - @visibleForTesting - static const String rerunErrorMessage = 'Failed to rerun task.'; - @visibleForTesting - static const String rerunSuccessMessage = 'Devicelab is rerunning the task. This can take a minute to propagate.'; - @visibleForTesting - static const Duration rerunSnackBarDuration = Duration(seconds: 15); - - /// A lookup table to define the [Icon] for this task, based on - /// the values returned by [TaskBox.effectiveTaskStatus]. - static const Map statusIcon = { - TaskBox.statusFailed: Icon(Icons.clear, color: Colors.red, size: 32), - TaskBox.statusNew: Icon(Icons.new_releases, color: Colors.blue, size: 32), - TaskBox.statusInProgress: Icon(Icons.autorenew, color: Colors.blue, size: 32), - TaskBox.statusSucceeded: Icon(Icons.check_circle, color: Colors.green, size: 32), - }; - - @override - Widget build(BuildContext context) { - final QualifiedTask qualifiedTask = QualifiedTask.fromTask(task); - - final DateTime? now = Now.of(context); - final DateTime createTime = DateTime.fromMillisecondsSinceEpoch(task.createTimestamp.toInt()); - final DateTime startTime = DateTime.fromMillisecondsSinceEpoch(task.startTimestamp.toInt()); - final DateTime endTime = DateTime.fromMillisecondsSinceEpoch(task.endTimestamp.toInt()); - - final Duration queueDuration = - task.startTimestamp == 0 ? now!.difference(createTime) : startTime.difference(createTime); - final Duration runDuration = task.endTimestamp == 0 ? now!.difference(startTime) : endTime.difference(startTime); - - /// There are 2 possible states for queue time: - /// 1. Task is waiting to be scheduled (in queue) - /// 2. Task has been scheduled (out of queue) - final String queueText = (task.status != TaskBox.statusNew) - ? 'Queue time: ${queueDuration.inMinutes} minutes' - : 'Queueing for ${queueDuration.inMinutes} minutes'; - - /// There are 3 possible states for the runtime: - /// 1. Task has not run yet (new) - /// 2. Task is running (in progress) - /// 3. Task ran (other status) - final String runText = (task.status == TaskBox.statusInProgress) - ? 'Running for ${runDuration.inMinutes} minutes' - : (task.status != TaskBox.statusNew) - ? 'Run time: ${runDuration.inMinutes} minutes' - : ''; - - final String summaryText = [ - 'Attempts: ${task.attempts}', - if (runText.isNotEmpty) runText, - queueText, - if (task.isFlaky) 'Flaky: ${task.isFlaky}', - ].join('\n'); - - return Card( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0), - child: IntrinsicWidth( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Tooltip( - message: task.status, - child: Padding( - padding: const EdgeInsets.only(left: 8.0, top: 10.0, right: 12.0), - child: statusIcon[task.status], - ), - ), - Expanded( - child: ListBody( - children: [ - SelectableText( - task.name, - style: Theme.of(context).textTheme.bodyLarge, - ), - Text( - summaryText, - style: Theme.of(context).textTheme.bodyMedium, - ), - if (QualifiedTask.fromTask(task).isLuci || QualifiedTask.fromTask(task).isDartInternal) - LuciTaskAttemptSummary(task: task), - ], - ), - ), - ], - ), - ), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - if (qualifiedTask.isLuci) - Padding( - padding: const EdgeInsets.only(left: 8.0), - // The RERUN button is only enabled if the user is authenticated. - child: AnimatedBuilder( - animation: buildState, - builder: (context, child) { - final bool isAuthenticated = buildState.authService.isAuthenticated; - return ProgressButton( - onPressed: isAuthenticated - ? () { - return _rerunTask(task); - } - : null, - child: child, - ); - }, - child: const Text('RERUN'), - ), - ), - ], - ), - ], - ), - ), - ), - ); - } - - Future _rerunTask(Task task) async { - final bool rerunResponse = await buildState.rerunTask(task); - if (rerunResponse) { - showSnackBarCallback( - const SnackBar( - content: Text(rerunSuccessMessage), - duration: rerunSnackBarDuration, - ), - ); - } - } -} diff --git a/dashboard/lib/widgets/user_sign_in.dart b/dashboard/lib/widgets/user_sign_in.dart deleted file mode 100644 index 92ff05119..000000000 --- a/dashboard/lib/widgets/user_sign_in.dart +++ /dev/null @@ -1,70 +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 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:google_sign_in/widgets.dart'; -import 'package:provider/provider.dart'; - -import '../service/google_authentication.dart'; -import 'sign_in_button/sign_in_button.dart'; - -enum _SignInButtonAction { logout } - -/// Widget for displaying sign in information for the current user. -/// -/// If logged in, it will display the user's avatar. Clicking it opens a dropdown for logging out. -/// Otherwise, a sign in button will show. -class UserSignIn extends StatelessWidget { - const UserSignIn({ - super.key, - }); - - @override - Widget build(BuildContext context) { - final GoogleSignInService authService = Provider.of(context); - - // Listen to the changes of `authService` to re-render. - return AnimatedBuilder( - animation: authService, - builder: (BuildContext context, _) { - if (authService.user != null) { - return PopupMenuButton<_SignInButtonAction>( - offset: const Offset(0, 50), - itemBuilder: (BuildContext context) => >[ - const PopupMenuItem<_SignInButtonAction>( - value: _SignInButtonAction.logout, - child: Text('Log out'), - ), - ], - onSelected: (_SignInButtonAction value) { - switch (value) { - case _SignInButtonAction.logout: - authService.signOut(); - break; - } - }, - iconSize: Scaffold.of(context).appBarMaxHeight, - icon: Builder( - builder: (BuildContext context) { - if (!kIsWeb && Platform.environment.containsKey('FLUTTER_TEST')) { - return Padding( - padding: const EdgeInsets.only(right: 10.0, top: 20.0), - child: Text(authService.user!.email), - ); - } - return GoogleUserCircleAvatar( - identity: authService.user!, - ); - }, - ), - ); - } - return const SignInButton(); - }, - ); - } -} diff --git a/dashboard/lib/widgets/web_image.dart b/dashboard/lib/widgets/web_image.dart deleted file mode 100644 index ab900926e..000000000 --- a/dashboard/lib/widgets/web_image.dart +++ /dev/null @@ -1,66 +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 'package:flutter/foundation.dart'; -import 'package:flutter/widgets.dart'; - -/// Helper widget for switching between a network image and a placeholder. -/// -/// This bypasses network image provider when testing to not surface the -/// HTTP errors in tests by default. -class WebImage extends StatelessWidget { - const WebImage({ - super.key, - bool? enabled, - this.imageUrl, - this.placeholder, - this.width = 50, - this.height = 50, - }) : _enabled = enabled; - - final bool? _enabled; - bool? get enabled { - // This being a getter is sketchy but it's ok because in any execution of this code, - // the value returned from this getter cannot change. - // If it was possible for this value to change over time then this would not be a - // valid way to write a widget and we would instead have to use a StatefulWidget. - if (_enabled != null) { - return _enabled; - } - // We have to use Platform.environment, not bool.fromEnvironment, because when the code is - // compiled, the environment does not contain the FLUTTER_TEST key, but when the code is - // executed as a test, it does. We have to check kIsWeb because the Platform.environment - // feature doesn't exist on Web. - if (!kIsWeb && Platform.environment.containsKey('FLUTTER_TEST')) { - return false; - } - return true; - } - - /// The url to fetch the image from. - final String? imageUrl; - - /// Widget to fall back to if environment does not support network images. - final Widget? placeholder; - - /// Height of the image. - final double width; - - /// Width of the image. - final double height; - - @override - Widget build(BuildContext context) { - if (enabled!) { - return Image.network( - imageUrl!, - width: width, - height: height, - ); - } - return placeholder!; - } -} diff --git a/dashboard/linux/.gitignore b/dashboard/linux/.gitignore deleted file mode 100644 index d3896c984..000000000 --- a/dashboard/linux/.gitignore +++ /dev/null @@ -1 +0,0 @@ -flutter/ephemeral diff --git a/dashboard/linux/CMakeLists.txt b/dashboard/linux/CMakeLists.txt deleted file mode 100644 index d02a9c5f1..000000000 --- a/dashboard/linux/CMakeLists.txt +++ /dev/null @@ -1,106 +0,0 @@ -cmake_minimum_required(VERSION 3.10) -project(runner LANGUAGES CXX) - -set(BINARY_NAME "app_flutter") -set(APPLICATION_ID "com.example.app_flutter") - -cmake_policy(SET CMP0063 NEW) - -set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") - -# Configure build options. -if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - set(CMAKE_BUILD_TYPE "Debug" CACHE - STRING "Flutter build mode" FORCE) - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS - "Debug" "Profile" "Release") -endif() - -# Compilation settings that should be applied to most targets. -function(APPLY_STANDARD_SETTINGS TARGET) - target_compile_features(${TARGET} PUBLIC cxx_std_14) - target_compile_options(${TARGET} PRIVATE -Wall -Werror) - target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") - target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") -endfunction() - -set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") - -# Flutter library and tool build rules. -add_subdirectory(${FLUTTER_MANAGED_DIR}) - -# System-level dependencies. -find_package(PkgConfig REQUIRED) -pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) - -add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") - -# Application build -add_executable(${BINARY_NAME} - "main.cc" - "my_application.cc" - "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" -) -apply_standard_settings(${BINARY_NAME}) -target_link_libraries(${BINARY_NAME} PRIVATE flutter) -target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) -add_dependencies(${BINARY_NAME} flutter_assemble) -# Only the install-generated bundle's copy of the executable will launch -# correctly, since the resources must in the right relative locations. To avoid -# people trying to run the unbundled copy, put it in a subdirectory instead of -# the default top-level location. -set_target_properties(${BINARY_NAME} - PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" -) - -# Generated plugin build rules, which manage building the plugins and adding -# them to the application. -include(flutter/generated_plugins.cmake) - - -# === Installation === -# By default, "installing" just makes a relocatable bundle in the build -# directory. -set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") -if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) -endif() - -# Start with a clean build bundle directory every time. -install(CODE " - file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") - " COMPONENT Runtime) - -set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") -set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") - -install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) - -if(PLUGIN_BUNDLED_LIBRARIES) - install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" - DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) -endif() - -# Fully re-copy the assets directory on each build to avoid having stale files -# from a previous install. -set(FLUTTER_ASSET_DIR_NAME "flutter_assets") -install(CODE " - file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") - " COMPONENT Runtime) -install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" - DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) - -# Install the AOT library on non-Debug builds only. -if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") - install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) -endif() diff --git a/dashboard/linux/flutter/CMakeLists.txt b/dashboard/linux/flutter/CMakeLists.txt deleted file mode 100644 index a1da1b9e5..000000000 --- a/dashboard/linux/flutter/CMakeLists.txt +++ /dev/null @@ -1,91 +0,0 @@ -cmake_minimum_required(VERSION 3.10) - -set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") - -# Configuration provided via flutter tool. -include(${EPHEMERAL_DIR}/generated_config.cmake) - -# TODO: Move the rest of this into files in ephemeral. See -# https://github.com/flutter/flutter/issues/57146. - -# Serves the same purpose as list(TRANSFORM ... PREPEND ...), -# which isn't available in 3.10. -function(list_prepend LIST_NAME PREFIX) - set(NEW_LIST "") - foreach(element ${${LIST_NAME}}) - list(APPEND NEW_LIST "${PREFIX}${element}") - endforeach(element) - set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) -endfunction() - -# === Flutter Library === -# System-level dependencies. -find_package(PkgConfig REQUIRED) -pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) -pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) -pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) -pkg_check_modules(BLKID REQUIRED IMPORTED_TARGET blkid) -pkg_check_modules(LZMA REQUIRED IMPORTED_TARGET liblzma) - -set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") - -# Published to parent scope for install step. -set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) -set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) -set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) -set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) - -list(APPEND FLUTTER_LIBRARY_HEADERS - "fl_basic_message_channel.h" - "fl_binary_codec.h" - "fl_binary_messenger.h" - "fl_dart_project.h" - "fl_engine.h" - "fl_json_message_codec.h" - "fl_json_method_codec.h" - "fl_message_codec.h" - "fl_method_call.h" - "fl_method_channel.h" - "fl_method_codec.h" - "fl_method_response.h" - "fl_plugin_registrar.h" - "fl_plugin_registry.h" - "fl_standard_message_codec.h" - "fl_standard_method_codec.h" - "fl_string_codec.h" - "fl_value.h" - "fl_view.h" - "flutter_linux.h" -) -list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") -add_library(flutter INTERFACE) -target_include_directories(flutter INTERFACE - "${EPHEMERAL_DIR}" -) -target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") -target_link_libraries(flutter INTERFACE - PkgConfig::GTK - PkgConfig::GLIB - PkgConfig::GIO - PkgConfig::BLKID - PkgConfig::LZMA -) -add_dependencies(flutter flutter_assemble) - -# === Flutter tool backend === -# _phony_ is a non-existent file to force this command to run every time, -# since currently there's no way to get a full input/output list from the -# flutter tool. -add_custom_command( - OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} - ${CMAKE_CURRENT_BINARY_DIR}/_phony_ - COMMAND ${CMAKE_COMMAND} -E env - ${FLUTTER_TOOL_ENVIRONMENT} - "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" - linux-x64 ${CMAKE_BUILD_TYPE} - VERBATIM -) -add_custom_target(flutter_assemble DEPENDS - "${FLUTTER_LIBRARY}" - ${FLUTTER_LIBRARY_HEADERS} -) diff --git a/dashboard/linux/flutter/generated_plugins.cmake b/dashboard/linux/flutter/generated_plugins.cmake deleted file mode 100644 index f16b4c342..000000000 --- a/dashboard/linux/flutter/generated_plugins.cmake +++ /dev/null @@ -1,24 +0,0 @@ -# -# Generated file, do not edit. -# - -list(APPEND FLUTTER_PLUGIN_LIST - url_launcher_linux -) - -list(APPEND FLUTTER_FFI_PLUGIN_LIST -) - -set(PLUGIN_BUNDLED_LIBRARIES) - -foreach(plugin ${FLUTTER_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) - target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) - list(APPEND PLUGIN_BUNDLED_LIBRARIES $) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) -endforeach(plugin) - -foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) -endforeach(ffi_plugin) diff --git a/dashboard/linux/main.cc b/dashboard/linux/main.cc deleted file mode 100644 index e7c5c5437..000000000 --- a/dashboard/linux/main.cc +++ /dev/null @@ -1,6 +0,0 @@ -#include "my_application.h" - -int main(int argc, char** argv) { - g_autoptr(MyApplication) app = my_application_new(); - return g_application_run(G_APPLICATION(app), argc, argv); -} diff --git a/dashboard/linux/my_application.cc b/dashboard/linux/my_application.cc deleted file mode 100644 index 0d4148eaa..000000000 --- a/dashboard/linux/my_application.cc +++ /dev/null @@ -1,104 +0,0 @@ -#include "my_application.h" - -#include -#ifdef GDK_WINDOWING_X11 -#include -#endif - -#include "flutter/generated_plugin_registrant.h" - -struct _MyApplication { - GtkApplication parent_instance; - char** dart_entrypoint_arguments; -}; - -G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) - -// Implements GApplication::activate. -static void my_application_activate(GApplication* application) { - MyApplication* self = MY_APPLICATION(application); - GtkWindow* window = - GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); - - // Use a header bar when running in GNOME as this is the common style used - // by applications and is the setup most users will be using (e.g. Ubuntu - // desktop). - // If running on X and not using GNOME then just use a traditional title bar - // in case the window manager does more exotic layout, e.g. tiling. - // If running on Wayland assume the header bar will work (may need changing - // if future cases occur). - gboolean use_header_bar = TRUE; -#ifdef GDK_WINDOWING_X11 - GdkScreen *screen = gtk_window_get_screen(window); - if (GDK_IS_X11_SCREEN(screen)) { - const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); - if (g_strcmp0(wm_name, "GNOME Shell") != 0) { - use_header_bar = FALSE; - } - } -#endif - if (use_header_bar) { - GtkHeaderBar *header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); - gtk_widget_show(GTK_WIDGET(header_bar)); - gtk_header_bar_set_title(header_bar, "app_flutter"); - gtk_header_bar_set_show_close_button(header_bar, TRUE); - gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); - } - else { - gtk_window_set_title(window, "app_flutter"); - } - - gtk_window_set_default_size(window, 1280, 720); - gtk_widget_show(GTK_WIDGET(window)); - - g_autoptr(FlDartProject) project = fl_dart_project_new(); - fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); - - FlView* view = fl_view_new(project); - gtk_widget_show(GTK_WIDGET(view)); - gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); - - fl_register_plugins(FL_PLUGIN_REGISTRY(view)); - - gtk_widget_grab_focus(GTK_WIDGET(view)); -} - -// Implements GApplication::local_command_line. -static gboolean my_application_local_command_line(GApplication* application, gchar ***arguments, int *exit_status) { - MyApplication* self = MY_APPLICATION(application); - // Strip out the first argument as it is the binary name. - self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); - - g_autoptr(GError) error = nullptr; - if (!g_application_register(application, nullptr, &error)) { - g_warning("Failed to register: %s", error->message); - *exit_status = 1; - return TRUE; - } - - g_application_activate(application); - *exit_status = 0; - - return TRUE; -} - -// Implements GObject::dispose. -static void my_application_dispose(GObject *object) { - MyApplication* self = MY_APPLICATION(object); - g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); - G_OBJECT_CLASS(my_application_parent_class)->dispose(object); -} - -static void my_application_class_init(MyApplicationClass* klass) { - G_APPLICATION_CLASS(klass)->activate = my_application_activate; - G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; - G_OBJECT_CLASS(klass)->dispose = my_application_dispose; -} - -static void my_application_init(MyApplication* self) {} - -MyApplication* my_application_new() { - return MY_APPLICATION(g_object_new(my_application_get_type(), - "application-id", APPLICATION_ID, - nullptr)); -} diff --git a/dashboard/linux/my_application.h b/dashboard/linux/my_application.h deleted file mode 100644 index 72271d5e4..000000000 --- a/dashboard/linux/my_application.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef FLUTTER_MY_APPLICATION_H_ -#define FLUTTER_MY_APPLICATION_H_ - -#include - -G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, - GtkApplication) - -/** - * my_application_new: - * - * Creates a new Flutter-based application. - * - * Returns: a new #MyApplication. - */ -MyApplication* my_application_new(); - -#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/dashboard/macos/.gitignore b/dashboard/macos/.gitignore deleted file mode 100644 index d2fd37723..000000000 --- a/dashboard/macos/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -# Flutter-related -**/Flutter/ephemeral/ -**/Pods/ - -# Xcode-related -**/xcuserdata/ diff --git a/dashboard/macos/Podfile b/dashboard/macos/Podfile deleted file mode 100644 index dade8dfad..000000000 --- a/dashboard/macos/Podfile +++ /dev/null @@ -1,40 +0,0 @@ -platform :osx, '10.11' - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug' => :debug, - 'Profile' => :release, - 'Release' => :release, -} - -def flutter_root - generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) - unless File.exist?(generated_xcode_build_settings_path) - raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" - end - - File.foreach(generated_xcode_build_settings_path) do |line| - matches = line.match(/FLUTTER_ROOT\=(.*)/) - return matches[1].strip if matches - end - raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" -end - -require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) - -flutter_macos_podfile_setup - -target 'Runner' do - use_frameworks! - use_modular_headers! - - flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) -end - -post_install do |installer| - installer.pods_project.targets.each do |target| - flutter_additional_macos_build_settings(target) - end -end diff --git a/dashboard/macos/Podfile.lock b/dashboard/macos/Podfile.lock deleted file mode 100644 index 5bab7f10d..000000000 --- a/dashboard/macos/Podfile.lock +++ /dev/null @@ -1,22 +0,0 @@ -PODS: - - FlutterMacOS (1.0.0) - - url_launcher_macos (0.0.1): - - FlutterMacOS - -DEPENDENCIES: - - FlutterMacOS (from `Flutter/ephemeral`) - - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) - -EXTERNAL SOURCES: - FlutterMacOS: - :path: Flutter/ephemeral - url_launcher_macos: - :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos - -SPEC CHECKSUMS: - FlutterMacOS: 57701585bf7de1b3fc2bb61f6378d73bbdea8424 - url_launcher_macos: 45af3d61de06997666568a7149c1be98b41c95d4 - -PODFILE CHECKSUM: 6eac6b3292e5142cfc23bdeb71848a40ec51c14c - -COCOAPODS: 1.11.2 diff --git a/dashboard/macos/Runner.xcodeproj/project.pbxproj b/dashboard/macos/Runner.xcodeproj/project.pbxproj deleted file mode 100644 index df9fe8011..000000000 --- a/dashboard/macos/Runner.xcodeproj/project.pbxproj +++ /dev/null @@ -1,632 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 51; - objects = { - -/* Begin PBXAggregateTarget section */ - 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { - isa = PBXAggregateTarget; - buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; - buildPhases = ( - 33CC111E2044C6BF0003C045 /* ShellScript */, - ); - dependencies = ( - ); - name = "Flutter Assemble"; - productName = FLX; - }; -/* End PBXAggregateTarget section */ - -/* Begin PBXBuildFile section */ - 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; - 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; - 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; - 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; - 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; - DDC65199A2B17643B3354089 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2C86CFDE45469440BEFBC0D2 /* Pods_Runner.framework */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 33CC10E52044A3C60003C045 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 33CC111A2044C6BA0003C045; - remoteInfo = FLX; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 33CC110E2044A8840003C045 /* Bundle Framework */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - ); - name = "Bundle Framework"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 037BDC810393B8995632E348 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - 2C86CFDE45469440BEFBC0D2 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; - 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* app_flutter.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = app_flutter.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; - 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; - 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; - 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; - 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; - 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; - 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; - 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; - 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; - 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; - 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; - C0820FDE7705A735A7DBA45E /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - E28A7AE7067AAF4AFCDC1616 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - 33CC10EA2044A3C60003C045 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - DDC65199A2B17643B3354089 /* Pods_Runner.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 33BA886A226E78AF003329D5 /* Configs */ = { - isa = PBXGroup; - children = ( - 33E5194F232828860026EE4D /* AppInfo.xcconfig */, - 9740EEB21CF90195004384FC /* Debug.xcconfig */, - 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, - 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, - ); - path = Configs; - sourceTree = ""; - }; - 33CC10E42044A3C60003C045 = { - isa = PBXGroup; - children = ( - 33FAB671232836740065AC1E /* Runner */, - 33CEB47122A05771004F2AC0 /* Flutter */, - 33CC10EE2044A3C60003C045 /* Products */, - D73912EC22F37F3D000D13A0 /* Frameworks */, - 38F37C5495EEC9302799D945 /* Pods */, - ); - sourceTree = ""; - }; - 33CC10EE2044A3C60003C045 /* Products */ = { - isa = PBXGroup; - children = ( - 33CC10ED2044A3C60003C045 /* app_flutter.app */, - ); - name = Products; - sourceTree = ""; - }; - 33CC11242044D66E0003C045 /* Resources */ = { - isa = PBXGroup; - children = ( - 33CC10F22044A3C60003C045 /* Assets.xcassets */, - 33CC10F42044A3C60003C045 /* MainMenu.xib */, - 33CC10F72044A3C60003C045 /* Info.plist */, - ); - name = Resources; - path = ..; - sourceTree = ""; - }; - 33CEB47122A05771004F2AC0 /* Flutter */ = { - isa = PBXGroup; - children = ( - 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, - 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, - 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, - 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, - ); - path = Flutter; - sourceTree = ""; - }; - 33FAB671232836740065AC1E /* Runner */ = { - isa = PBXGroup; - children = ( - 33CC10F02044A3C60003C045 /* AppDelegate.swift */, - 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, - 33E51913231747F40026EE4D /* DebugProfile.entitlements */, - 33E51914231749380026EE4D /* Release.entitlements */, - 33CC11242044D66E0003C045 /* Resources */, - 33BA886A226E78AF003329D5 /* Configs */, - ); - path = Runner; - sourceTree = ""; - }; - 38F37C5495EEC9302799D945 /* Pods */ = { - isa = PBXGroup; - children = ( - C0820FDE7705A735A7DBA45E /* Pods-Runner.debug.xcconfig */, - E28A7AE7067AAF4AFCDC1616 /* Pods-Runner.release.xcconfig */, - 037BDC810393B8995632E348 /* Pods-Runner.profile.xcconfig */, - ); - name = Pods; - path = Pods; - sourceTree = ""; - }; - D73912EC22F37F3D000D13A0 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 2C86CFDE45469440BEFBC0D2 /* Pods_Runner.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 33CC10EC2044A3C60003C045 /* Runner */ = { - isa = PBXNativeTarget; - buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; - buildPhases = ( - 725778F93857FFE67F73B1B1 /* [CP] Check Pods Manifest.lock */, - 33CC10E92044A3C60003C045 /* Sources */, - 33CC10EA2044A3C60003C045 /* Frameworks */, - 33CC10EB2044A3C60003C045 /* Resources */, - 33CC110E2044A8840003C045 /* Bundle Framework */, - 3399D490228B24CF009A79C7 /* ShellScript */, - 99A15D91199BB8673AF3769F /* [CP] Embed Pods Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - 33CC11202044C79F0003C045 /* PBXTargetDependency */, - ); - name = Runner; - productName = Runner; - productReference = 33CC10ED2044A3C60003C045 /* app_flutter.app */; - productType = "com.apple.product-type.application"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 33CC10E52044A3C60003C045 /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 0930; - ORGANIZATIONNAME = ""; - TargetAttributes = { - 33CC10EC2044A3C60003C045 = { - CreatedOnToolsVersion = 9.2; - LastSwiftMigration = 1100; - ProvisioningStyle = Automatic; - SystemCapabilities = { - com.apple.Sandbox = { - enabled = 1; - }; - }; - }; - 33CC111A2044C6BA0003C045 = { - CreatedOnToolsVersion = 9.2; - ProvisioningStyle = Manual; - }; - }; - }; - buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; - compatibilityVersion = "Xcode 9.3"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 33CC10E42044A3C60003C045; - productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 33CC10EC2044A3C60003C045 /* Runner */, - 33CC111A2044C6BA0003C045 /* Flutter Assemble */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 33CC10EB2044A3C60003C045 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, - 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 3399D490228B24CF009A79C7 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; - }; - 33CC111E2044C6BF0003C045 /* ShellScript */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - Flutter/ephemeral/FlutterInputs.xcfilelist, - ); - inputPaths = ( - Flutter/ephemeral/tripwire, - ); - outputFileListPaths = ( - Flutter/ephemeral/FlutterOutputs.xcfilelist, - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; - }; - 725778F93857FFE67F73B1B1 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - 99A15D91199BB8673AF3769F /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 33CC10E92044A3C60003C045 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, - 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, - 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; - targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { - isa = PBXVariantGroup; - children = ( - 33CC10F52044A3C60003C045 /* Base */, - ); - name = MainMenu.xib; - path = Runner; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - 338D0CE9231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - }; - name = Profile; - }; - 338D0CEA231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 5.0; - }; - name = Profile; - }; - 338D0CEB231458BD00FA5F75 /* Profile */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Profile; - }; - 33CC10F92044A3C60003C045 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = macosx; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - 33CC10FA2044A3C60003C045 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CODE_SIGN_IDENTITY = "-"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = macosx; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_OPTIMIZATION_LEVEL = "-O"; - }; - name = Release; - }; - 33CC10FC2044A3C60003C045 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - }; - name = Debug; - }; - 33CC10FD2044A3C60003C045 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; - CODE_SIGN_STYLE = Automatic; - COMBINE_HIDPI_IMAGES = YES; - INFOPLIST_FILE = Runner/Info.plist; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/../Frameworks", - ); - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 5.0; - }; - name = Release; - }; - 33CC111C2044C6BA0003C045 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Manual; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Debug; - }; - 33CC111D2044C6BA0003C045 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CODE_SIGN_STYLE = Automatic; - PRODUCT_NAME = "$(TARGET_NAME)"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC10F92044A3C60003C045 /* Debug */, - 33CC10FA2044A3C60003C045 /* Release */, - 338D0CE9231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC10FC2044A3C60003C045 /* Debug */, - 33CC10FD2044A3C60003C045 /* Release */, - 338D0CEA231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 33CC111C2044C6BA0003C045 /* Debug */, - 33CC111D2044C6BA0003C045 /* Release */, - 338D0CEB231458BD00FA5F75 /* Profile */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 33CC10E52044A3C60003C045 /* Project object */; -} diff --git a/dashboard/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/dashboard/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003..000000000 --- a/dashboard/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/dashboard/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/dashboard/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme deleted file mode 100644 index fc6f156d2..000000000 --- a/dashboard/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/dashboard/macos/Runner.xcworkspace/contents.xcworkspacedata b/dashboard/macos/Runner.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 21a3cc14c..000000000 --- a/dashboard/macos/Runner.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - diff --git a/dashboard/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/dashboard/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist deleted file mode 100644 index 18d981003..000000000 --- a/dashboard/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist +++ /dev/null @@ -1,8 +0,0 @@ - - - - - IDEDidComputeMac32BitWarning - - - diff --git a/dashboard/macos/Runner/AppDelegate.swift b/dashboard/macos/Runner/AppDelegate.swift deleted file mode 100644 index d53ef6437..000000000 --- a/dashboard/macos/Runner/AppDelegate.swift +++ /dev/null @@ -1,9 +0,0 @@ -import Cocoa -import FlutterMacOS - -@NSApplicationMain -class AppDelegate: FlutterAppDelegate { - override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { - return true - } -} diff --git a/dashboard/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/dashboard/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index a2ec33f19..000000000 --- a/dashboard/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "images" : [ - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_16.png", - "scale" : "1x" - }, - { - "size" : "16x16", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "2x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_32.png", - "scale" : "1x" - }, - { - "size" : "32x32", - "idiom" : "mac", - "filename" : "app_icon_64.png", - "scale" : "2x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_128.png", - "scale" : "1x" - }, - { - "size" : "128x128", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "2x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_256.png", - "scale" : "1x" - }, - { - "size" : "256x256", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "2x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_512.png", - "scale" : "1x" - }, - { - "size" : "512x512", - "idiom" : "mac", - "filename" : "app_icon_1024.png", - "scale" : "2x" - } - ], - "info" : { - "version" : 1, - "author" : "xcode" - } -} diff --git a/dashboard/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/dashboard/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png deleted file mode 100644 index 3c4935a7ca84f0976aca34b7f2895d65fb94d1ea..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 46993 zcmZ5|3p`X?`~OCwR3s6~xD(})N~M}fiXn6%NvKp3QYhuNN0*apqmfHdR7#ShNQ99j zQi+P9nwlXbmnktZ_WnO>bl&&<{m*;O=RK!cd#$zCdM@AR`#jH%+2~+BeX7b-48x|= zZLBt9*d+MZNtpCx_&asa{+CselLUV<<&ceQ5QfRjLjQDSL-t4eq}5znmIXDtfA|D+VRV$*2jxU)JopC)!37FtD<6L^&{ia zgVf1p(e;c3|HY;%uD5<-oSFkC2JRh- z&2RTL)HBG`)j5di8ys|$z_9LSm^22*uH-%MmUJs|nHKLHxy4xTmG+)JoA`BN7#6IN zK-ylvs+~KN#4NWaH~o5Wuwd@W?H@diExdcTl0!JJq9ZOA24b|-TkkeG=Q(pJw7O;i z`@q+n|@eeW7@ z&*NP+)wOyu^5oNJ=yi4~s_+N)#M|@8nfw=2#^BpML$~dJ6yu}2JNuq!)!;Uwxic(z zM@Wa-v|U{v|GX4;P+s#=_1PD7h<%8ey$kxVsS1xt&%8M}eOF98&Rx7W<)gY(fCdmo{y*FPC{My!t`i=PS1cdV7DD=3S1J?b2<5BevW7!rWJ%6Q?D9UljULd*7SxX05PP^5AklWu^y` z-m9&Oq-XNSRjd|)hZ44DK?3>G%kFHSJ8|ZXbAcRb`gH~jk}Iwkl$@lqg!vu)ihSl= zjhBh%%Hq|`Vm>T7+SYyf4bI-MgiBq4mZlZmsKv+S>p$uAOoNxPT)R6owU%t*#aV}B z5@)X8nhtaBhH=={w;Du=-S*xvcPz26EI!gt{(hf;TllHrvku`^8wMj7-9=By>n{b= zHzQ?Wn|y=;)XM#St@o%#8idxfc`!oVz@Lv_=y(t-kUC`W)c0H2TX}Lop4121;RHE(PPHKfe_e_@DoHiPbVP%JzNudGc$|EnIv`qww1F5HwF#@l(=V zyM!JQO>Rt_PTRF1hI|u^2Uo#w*rdF*LXJky0?|fhl4-M%zN_2RP#HFhSATE3&{sos zIE_?MdIn!sUH*vjs(teJ$7^7#|M_7m`T>r>qHw>TQh?yhhc8=TJk2B;KNXw3HhnQs za(Uaz2VwP;82rTy(T3FJNKA86Y7;L(K=~BW_Q=jjRh=-k_=wh-$`nY+#au+v^C4VV z)U?X(v-_#i=3bAylP1S*pM_y*DB z2fR!imng6Dk$>dl*K@AIj<~zw_f$T!-xLO8r{OkE(l?W#W<={460Y02*K#)O4xp?W zAN+isO}!*|mN7B#jUt&!KNyFOpUxv&ybM>jmkfn8z^llBslztv!!`TBEPwu;#eR3d z@_VDa)|ByvXx1V=^Up4{;M8ji3FC7gm(C7Ty-#1gs+U<{Ouc(iV67{< zam#KwvR&s=k4W<13`}DxzJ9{TUa97N-cgWkCDc+C339)EEnC@^HQK6OvKDSCvNz(S zOFAF_6omgG!+zaPC8fBO3kH8YVBx9_AoM?->pv~@$saf(Myo|e@onD`a=;kO*Utem ze=eUH&;JB2I4}?Pm@=VnE+yb$PD~sA5+)|iH3bi|s?ExIePeoAMd(Z4Z%$mCu{t;B9(sgdG~Q}0ShAwe!l8nw0tJn zJ+m?ogrgty$3=T&6+JJa!1oS3AtQQ1gJ z3gR1<=hXU>{SB-zq!okl4c+V9N;vo4{fyGeqtgBIt%TPC1P&k!pR-GZ7O8b}9=%>3 zQrV%FQdB+CcCRKK)0}v>U25rbQk(1^9Ax|WcAo5?L(H&H@%zAoT2RH$iN6boyXpsYqME}WJZI6T%OMlkWXK>R`^7AHG&31 z&MIU}igQ7$;)7AEm#dXA+!I&6ymb7n6D;F7c$tO3Ql(`ht z1sFrzIk_q5#=!#D(e~#SdWz5K;tPF*R883Yu>*@jTeOGUjQekw zM+7HlfP{y8p}jA9bLfyKC_Ti8k#;AVp@RML^9MQp-E+Ns-Y zKA!aAZV-sfm<23fy#@TZZlQVQxH%R7rD}00LxHPUF!Yg3%OX ziDe4m<4fp{7ivBS?*AlJz$~vw5m)Ei8`|+~xOSqJ$waA0+Yys$z$9iN9TIXu8 zaYacjd09uRAsU|)g|03w`F|b1Xg#K~*Mp2X^K^)r3P^juoc}-me&YhkW3#G|H<~jK zoKD?lE@jOw7>4cpKkh!8qU!bF(i~Oa8a!EGy-j46eZYbKUvF=^^nq`EtWFK}gwrsB zeu<6~?mk+;+$whP)8ud8vjqh+NofU+Nu`~|pb&CN1y_idxxf6cGbT=fBZR_hl&G)GgnW$*oDrN-zz;cKs18n+dAn95w z)Y>l6!5eYpebJGw7it~Q5m}8$7@%p&KS=VtydFj4HPJ{xqUVS_Ih}c(^4nUdwG|0% zw8Fnm{IT`8MqoL(1BNtu_#7alS@3WSUUOFT@U*`V!zrPIeCbbO=pE%|g92$EU|lw; z^;^AqMVWVf-R5^OI79TzIyYf}HX%0Y)=aYH;EKo}?=R~ZM&s&F;W>u%hFUfNafb;- z8OkmkK3k||J#3`xdLuMJAhj9oPI?Cjt}cDN7hw26n7irWS0hsy`fs&Y?Y&(QF*Nu! z!p`NggHXaBU6$P42LkqnKsPG@363DHYGXg{!|z6VMAQt??>FK1B4x4{j;iY8A+7o% z*!0qt&w+w#Ob@pQp;q)u0;v^9FlY=AK>2!qku)!%TO<^lNBr!6R8X)iXgXi^1p`T8 z6sU@Y_Fsp6E89E1*jz~Tm2kF=mjYz_q99r^v0h-l7SP6azzL%woM6!7>IFWyizrNwAqoia3nN0q343q zFztMPh0)?ugQg5Izbk{5$EGcMzt*|=S8ZFK%O&^YV@V;ZRL>f!iG?s5z{(*Xq20c^ z(hkk~PljBo%U`$q>mz!ir7chKlE-oHA2&0i@hn4O5scsI&nIWsM>sYg;Ph5IO~VpT z%c-3_{^N>4kECzk?2~Z@V|jWio&a&no;boiNxqXOpS;ph)gEDFJ6E=zPJ$>y5w`U0 z;h9_6ncIEY?#j1+IDUuixRg&(hw+QSSEmFi%_$ua$^K%(*jUynGU@FlvsyThxqMRw z7_ALpqTj~jOSu2_(@wc_Z?>X&(5jezB6w-@0X_34f&cZ=cA-t%#}>L7Q3QRx1$qyh zG>NF=Ts>)wA)fZIlk-kz%Xa;)SE(PLu(oEC8>9GUBgd$(^_(G6Y((Hi{fsV; zt*!IBWx_$5D4D&ezICAdtEU!WS3`YmC_?+o&1RDSfTbuOx<*v`G<2SP;5Q4TqFV&q zJL=90Lcm^TL7a9xck}XPMRnQ`l0%w-fi@bRI&c*VDj!W4nj=qaQd$2U?^9RTT{*qS_)Q9OL>s}2P3&da^Pf(*?> z#&2bt;Q7N2`P{{KH@>)Tf5&za?crRmQ%8xZi<9f=EV3={K zwMet=oA0-@`8F;u`8j-!8G~0TiH5yKemY+HU@Zw3``1nT>D ziK465-m?Nm^~@G@RW2xH&*C#PrvCWU)#M4jQ`I*>_^BZB_c!z5Wn9W&eCBE(oc1pw zmMr)iu74Xl5>pf&D7Ml>%uhpFGJGyj6Mx=t#`}Mt3tDZQDn~K`gp0d)P>>4{FGiP$sPK*ExVs!1)aGgAX z6eA;-9@@Muti3xYv$8U{?*NxlHxs?)(6%!Iw&&l79K86h+Z8;)m9+(zzX?cS zH*~)yk)X^H1?AfL!xctY-8T0G0Vh~kcP=8%Wg*zZxm*;eb)TEh&lGuNkqJib_}i;l z*35qQ@}I#v;EwCGM2phE1{=^T4gT63m`;UEf5x2Get-WSWmt6%T6NJM`|tk-~4<#HHwCXuduB4+vW!BywlH8murH@|32CNxx7} zAoF?Gu02vpSl|q1IFO0tNEvKwyH5V^3ZtEO(su1sIYOr{t@Tr-Ot@&N*enq;Je38} zOY+C1bZ?P~1=Qb%oStI-HcO#|WHrpgIDR0GY|t)QhhTg*pMA|%C~>;R4t_~H1J3!i zyvQeDi&|930wZlA$`Wa9)m(cB!lPKD>+Ag$5v-}9%87`|7mxoNbq7r^U!%%ctxiNS zM6pV6?m~jCQEKtF3vLnpag``|bx+eJ8h=(8b;R+8rzueQvXgFhAW*9y$!DgSJgJj% zWIm~}9(R6LdlXEg{Y3g_i7dP^98=-3qa z$*j&xC_$5btF!80{D&2*mp(`rNLAM$JhkB@3al3s=1k^Ud6HHontlcZw&y?`uPT#a za8$RD%e8!ph8Ow7kqI@_vd7lgRhkMvpzp@4XJ`9dA@+Xk1wYf`0Dk!hIrBxhnRR(_ z%jd(~x^oqA>r>`~!TEyhSyrwNA(i}={W+feUD^8XtX^7^Z#c7att{ot#q6B;;t~oq zct7WAa?UK0rj0yhRuY$7RPVoO29JV$o1Z|sJzG5<%;7pCu%L-deUon-X_wAtzY@_d z6S}&5xXBtsf8TZ13chR&vOMYs0F1?SJcvPn>SFe#+P3r=6=VIqcCU7<6-vxR*BZUm zO^DkE{(r8!e56)2U;+8jH4tuD2c(ptk0R{@wWK?%Wz?fJckr9vpIU27^UN*Q$}VyHWx)reWgmEls}t+2#Zm z_I5?+htcQl)}OTqF<`wht89>W*2f6e)-ewk^XU5!sW2A2VtaI=lggR&I z;Rw{xd)WMqw`VUPbhrx!!1Eg_*O0Si6t@ny)~X^Gu8wZZDockr)5)6tm+<=z+rYu? zCof+;!nq6r9MAfh zp4|^2w^-3vFK~{JFX|F5BIWecBJkkEuE%iP8AZ z^&e|C+VEH&i(4Y|oWPCa#C3T$129o5xaJa=y8f(!k&q+x=M|rq{?Zw_n?1X-bt&bP zD{*>Io`F4(i+5eE2oEo6iF}jNAZ52VN&Cp>LD{MyB=mCeiwP+v#gRvr%W)}?JBTMY z_hc2r8*SksC%(pp$KGmWSa|fx;r^9c;~Q(Jqw1%;$#azZf}#Fca9NZOh{*YxV9(1ivVA^2Wz>!A&Xvmm-~{y8n!^Jdl8c>`J#=2~!P{ zC1g_5Ye3={{fB`R%Q|%9<1p1;XmPo5lH5PHvX$bCIYzQhGqj7hZ?@P4M0^mkejD|H zVzARm7LRy|8`jSG^GpxRIs=aD>Y{Cb>^IwGEKCMd5LAoI;b{Q<-G}x*e>86R8dNAV z<@jb1q%@QQanW1S72kOQ$9_E#O?o}l{mHd=%Dl{WQcPio$baXZN!j{2m)TH1hfAp{ zM`EQ=4J`fMj4c&T+xKT!I0CfT^UpcgJK22vC962ulgV7FrUrII5!rx1;{@FMg(dIf zAC}stNqooiVol%%TegMuWnOkWKKA}hg6c)ssp~EnTUVUI98;a}_8UeTgT|<%G3J=n zKL;GzAhIQ_@$rDqqc1PljwpfUwiB)w!#cLAkgR_af;>}(BhnC9N zqL|q8-?jsO&Srv54TxVuJ=rfcX=C7{JNV zSmW@s0;$(#!hNuU0|YyXLs{9$_y2^fRmM&g#toh}!K8P}tlJvYyrs6yjTtHU>TB0} zNy9~t5F47ocE_+%V1(D!mKNBQc{bnrAbfPC2KO?qdnCv8DJzEBeDbW}gd!g2pyRyK`H6TVU^~K# z488@^*&{foHKthLu?AF6l-wEE&g1CTKV|hN7nP+KJnkd0sagHm&k{^SE-woW9^fYD z7y?g*jh+ELt;$OgP>Se3o#~w9qS}!%#vBvB?|I-;GM63oYrJ}HFRW6D+{54v@PN8K z2kG8`!VVc+DHl^8y#cevo4VCnTaPTzCB%*)sr&+=p{Hh#(MwaJbeuvvd!5fd67J_W za`oKxTR=mtM7P}i2qHG8=A(39l)_rHHKduDVA@^_Ueb7bq1A5#zHAi**|^H@fD`_W z#URdSG86hhQ#&S-Vf_8b`TIAmM55XhaHX7}Ci-^(ZDs*yb-WrWV&(oAQu3vMv%u$5 zc;!ADkeNBN_@47r!;%G3iFzo;?k)xTS-;1D-YeS5QXN7`p2PzGK~e6ib;8COBa5)p zfMn}dA--&A12~zr&GVk?qnBGfIEo`5yir;-Q;ZLn{Fimdrk;e!)q`sAkYh^~^>4Q@ zN5RT>s38+`V{|6@k&vZW!W0*BEqV&~34d+Ev8h)ObYL7Bd_hgbUzjdJaXP=S@Dp6X z)i013q3K4Gr5d%2YIp>218pYK!xwH;k)j?uUrT-yVKLg*L3y~=a+qd!RWGTL`z>29 z-Zb4Y{%pT%`R-iA#?T58c-i@?jf-Ckol9O>HAZPUxN%Z=<4ad9BL7n`_kH0i#E(m& zaNb039+z~ONUCLsf_a|x*&ptU?`=R*n}rm-tOdCDrS!@>>xBg)B3Sy8?x^e=U=i8< zy7H-^BPfM}$hf*d_`Qhk_V$dRYZw<)_mbC~gPPxf0$EeXhl-!(ZH3rkDnf`Nrf4$+ zh?jsRS+?Zc9Cx7Vzg?q53ffpp43po22^8i1Obih&$oBufMR;cT2bHlSZ#fDMZZr~u zXIfM5SRjBj4N1}#0Ez|lHjSPQoL&QiT4mZn=SxHJg~R`ZjP!+hJ?&~tf$N!spvKPi zfY;x~laI9X`&#i#Z}RJ`0+MO_j^3#3TQJu2r;A-maLD8xfI+2Y*iDf4LsQ$9xiu?~ z?^wHEf^qlgtjdj(u_(W5sbGx1;maVPDHvI-76u2uUywf;>()=e>0le;bO0LIvs)iy z*lJTO+7gyf^)2uS-PhS_O-+RToQmc6VT>ej^y^stNkwIxUg?E|YMAAwQ}U!dC&cXL ziXKU?zT~xbh6C};rICGbdX~;8Z%L~Jdg|`senVEJo-CiDsX47Kc`;EiXWO<9o)(`4 zGj(9@c+Me=F~y(HUehcAy!tkoM&e1y#(qqCkE(0lik_U>wg8vOhGR(=gBGFSbR`mh zn-%j3VTD4 zwA1Kqw!OSgi_v0;6?=Bk4Z{l-7Fl4`ZT535OC{73{rBwpNHMPH>((4G`sh zZhr!v{zM@4Q$5?8)Jm;v$A2v$Yp9qFG7y`9j7O-zhzC+7wr3Cb8sS$O{yOFOODdL) zV2pU{=nHne51{?^kh%a$WEro~o(rKQmM!p?#>5Pt`;!{0$2jkmVzsl|Nr^UF^IHxG z8?HmZEVMY~ec%Ow6hjfg6!9hCC4xY?V;5Ipo-myV=3TmfT^@XkKME`+=_inm4h7ki z->K~a+20?)zic^zc&7h=0)T{Aa24FU_}(O|9DMW3Bf>MW=O%~8{unFxp4}B+>>_KN zU%rKs3Va&&27&OX4-o&y2ie|sN2p-=S^V<2wa2NUQ4)?0e|hgna*1R7(#R_ys3xmG zE#(ry+q=O~&t|RX@ZMD`-)0QmE*x%SBc(Yvq60JtCQ4RL(gdA(@=}0rYo5yKz36bW zkvLOosP6I?7qH!rce(}q@cH-{oM2ThKV2RZe+{{25hkc?T>=Tky12xHr0jmfH@SZi zLHPJ@^Oo^Zo%`gZk_hrbCzS+t|=O!Bt zWi|>M8mz~sD|Z>C1ZPf_Cs&R!S5E2qK+@j*UpP>;5_|+h+y{gb=zub7#QKSUabet# zFH2H0ul;zO+uc+V=W_W@_Ig-791T7J9&=5)wrBE?JEHS_A6P~VQ)u6s1)Pu|VxP(aYJV*(e<)(42R zm3AK>dr1QLbC1RMoQ|M5k+TWBjY9q+_vY=K-tUte35m4RWl51A<4O0ptqV3)KzL7U z0gpp-I1)|zvtA8V7-e-o9H)lB_Rx6;Bu7A2yE)6)SuDqWDs}~Ojfk?DFwI% z3E1(>LbbB7I(&E@B7nlulhvY=Wa1mGXD@ijD7WF^y@L1e55h)-hzoq}eWe!fh9m3V{)x^6F8?ed1z>+4;qW6A4hYYj zZCYP=c#I8+$pAIVyiY*#%!j3ySAnH`tp|=^lh{)#JimWaP_rXK40A0WcsEUj`G1}O zG?XQ~qK4F!lqauv6-BL_Up3+-l1=kVfD;D*C)yr>o9>W=%mIyATtn_OBLK+h@p)j5jRAb;m&Ok?TZH-5Q)~#UwdYFp~rEE{judWa9E)z zE>135C-xMdHYY&AZGR)tb`K}s0CK9 z1!))p^ZaUC*e50t`sL+)@`)#kJ}?C_cCMH@k{f4wh~0`OFnGQ2nzUuuu;=r4BYRcI z){G#a6Y$S(mIc6B#YS;jFcU{0`c)Raa$nG+hV(K|2|^ZWOI566zlF0N;t~$jD<_AX zjnD?HN-G>xRmHwtL3BcJX7)Q^YGfc?cS4Nj=yYl5MB(uBD?r@VTB|mIYs=au$e)e{ zLHWd!+EN*v2*(=y%G1JzyQdY&%|?~R5NPb)`S2dw1AJW8O;L=p?yVxJs=X?U#-l1O zk6xh8yyY;OTR7aF{P=kQ>y`*EFivnw%rQioA-I67WS+~hVamG4_sI)(Jo4vHS|@F@ zqrBHbxHd_Y8+?8Gfq=Z1O^Fs5moGayCHVUHY^8)^j)Aj*RB!S2-FA?4#-`puwBW`` zJ_6OQj(FGo8DotHYRKq;;$4xDn9=4rgw}5xvxhi)?n?W5{*%4%h9Tg)zlQl&fN~Z1)gL(Dn7X!P428I zwA+U-x5!cQ57g1N=2bLqAWF z!&cbvsD)dvYoqP5vaQz%rL@kv*J>0AMzWAKn~Mxi5g2GlI7qvVZo)Z5oj=#O!M&*O z`3O3)uvrjNTeremC}nW@(m%#E-sITB>j-!yBM#(=FN`~c#@XjL3e)SjR9&%QO%tUg zzGv=SLH()`ZIt?Ayym;9VG1Muq+a+7Zo+59?SuRu_`k>@S4!yS3roMnq+SDO?`C7V#2 z8vHf4&0k;{kLT)fa==7EILSu3e|ZnxtFO;1 zGqP-;Xo(>_QKcYUhsi-X72BqH#7Zb-TsiNIF>G9xOHT3XoA*qX^10+#XCU0)UO4_%A_s_vO=uDd3_Q%D{OsvLMW9wGvuuRnF52{2vH06D~7N672!bIMt@it_D}& zwjZ7gV!RzZ86*wbEB5cnMJRbEqMM{G!K)bfJjyPH^9nGnrOI9S{~!dm4~P#&b*~)h zCMwM8mR+y5i~E5*JAopwZ>F`=ORfA&IF%O8(aS<}^H6wcY1g^=lYLPtFpyvW9F z3;FCS-TGFYPr#Y$ue>}?rTYrmWr^VbUu>!eL$cEdh1e>5_UDnZ@Mu$l*KVo_NDEu^ zBn*!qVnzYv>t|<(>nt8%CoNPhN!qGP|sANRN^#+2YSSYHa>R1mss->c0f=#g@U58@? zA4sUbrA7)&KrTddS0M6pTSRaz)wqUgsT3&8-0eG|d;ULOUztdaiD3~>!10H`rRHWY z1iNu6=UaA8LUBoaH9G*;m`Mzm6d1d+A#I8sdkl*zfvbmV0}+u` zDMv=HJJm?IOwbP;f~yn|AI_J7`~+5&bPq6Iv?ILo2kk$%vIlGsI0%nf1z9Mth8cy! zWumMn=RL1O9^~bVEFJ}QVvss?tHIwci#ldC`~&KFS~DU5K5zzneq_Q91T~%-SVU4S zJ6nVI5jeqfh~*2{AY#b(R*Ny95RQBGIp^fxDK{I9nG0uHCqc-Ib;pUUh$t0-4wX*< z=RzW~;iR3xfRnW<>5Jr5O1MP)brA3+ei@H8Hjkt7yuYIpd7c-4j%U=8vn8HD#TPJo zSe+7~Db}4U3Y^4dl1)4XuKZ67f(ZP;?TYg9te>hbAr4R_0K$oq3y5m-gb?fR$UtF9 zS~S^=aDyFSE}9W2;Okj%uoG-Um^&Qo^bB#!W?|%=6+P>``bumeA2E7ti7Aj%Fr~qm z2gbOY{WTyX$!s5_0jPGPQQ0#&zQ0Zj0=_74X8|(#FMzl`&9G_zX*j$NMf?i3M;FCU z6EUr4vnUOnZd`*)Uw#6yI!hSIXr%OF5H z5QlF8$-|yjc^Y89Qfl!Er_H$@khM6&N*VKjIZ15?&DB?);muI`r;7r0{mI03v9#31 z#4O*vNqb=1b}TjLY`&ww@u^SE{4ZiO=jOP3!|6cKUV2*@kI9Aw0ASwn-OAV~0843$1_FGl7}eF6C57dJb3grW)*jtoUd zpqXvfJSCIv4G*_@XZE?> z4Lt=jTSc*hG3`qVq!PVMR2~G-1P{%amYoIg!8Odf4~nv6wnEVrBt-R5Au=g~4=X|n zHRJGVd|$>4@y#w;g!wz>+z%x?XM^xY%iw%QoqY@`vSqg0c>n_}g^lrV))+9n$zGOP zs%d&JWT2Jjxaz`_V%XtANP$#kLLlW=OG2?!Q%#ThY#Sj}*XzMsYis2HiU2OlfeC>d z8n8j-{Npr1ri$Jv2E_QqKsbc$6vedBiugD~S`_0QjTTtX(mS}j6)6e;xdh*sp5U0aMpuN}qTP=^_Qn zh~0padPWs&aXmf6b~}{7Raglc)$~p?G89N4)&a}`izf|bA)IUmFLQ8UM$T!6siQxr z=%)pPsWYXWCNdGMS3fK6cxVuhp7>mug|>DVtxGd~O8v@NFz<+l`8^#e^KS3})bovWb^ zILp4a_9#%Y*b6m$VH8#)2NL@6a9|q!@#XOXyU-oAe)RR$Auj6?p2LEp*lD!KP{%(- z@5}`S$R)Kxf@m68b}Tr7eUTO=dh2wBjlx;PuO~gbbS2~9KK1szxbz$R|Frl8NqGn= z2RDp@$u5Obk&sxp!<;h=C=ZKPZB+jk zBxrCc_gxabNnh6Gl;RR6>Yt8c$vkv>_o@KDMFW1bM-3krWm|>RG>U`VedjCz2lAB1 zg(qb_C@Z~^cR=_BmGB@f;-Is3Z=*>wR2?r({x}qymVe?YnczkKG%k?McZ2v3OVpT* z(O$vnv}*Tle9WVK_@X@%tR^Z!3?FT_3s@jb3KBVf#)4!p~AFGgmn%1fBbZe3T53$_+UX_A!@Kz63qSLeH@8(augJDJ;RA>6rNxQYkd6t(sqK=*zv4j;O#N(%*2cdD z3FjN6`owjbF%UFbCO=haP<;Y1KozVgUy(nnnoV7{_l5OYK>DKEgy%~)Rjb0meL49X z7Fg;d!~;Wh63AcY--x{1XWn^J%DQMg*;dLKxs$;db`_0so$qO!>~yPDNd-CrdN!ea zMgHt24mD%(w>*7*z-@bNFaTJlz;N0SU4@J(zDH*@!0V00y{QfFTt>Vx7y5o2Mv9*( z1J#J27gHPEI3{!^cbKr^;T8 z{knt%bS@nrExJq1{mz2x~tc$Dm+yw=~vZD|A3q>d534za^{X9e7qF29H5yu};J)vlJkKq}< zXObu*@ioXGp!F=WVG3eUtfIA$GGgv0N?d&3C47`Zo)ms*qO}A9BAEke!nh#AfQ0d_ z&_N)E>5BsoR0rPqZb)YN}b~6Ppjyev;MMis-HkWF!az%G? z#&it84hv!%_Q>bnwch!nZKxB05M=jgiFaB^M=e-sj1xR?dPYUzZ#jua`ggyCAcWY> z-L$r#a{=;JP5X}9(ZPC&PdG~h5>_8SueX($_)Qu(;()N3*ZQH(VGnkWq^C}0r)~G3_?a10y*LsFz zokU5AKsW9DUr-ylK61shLS#4@vPcteK-Ga9xvRnPq=xSD_zC=Q_%6IuM?GpL(9aDx z|8d_;^6_D4{IQ1ndMAcFz5ZaT+Ww0wWN`xP(U#^=POs(BpKm;(H(lmYp+XCb7Kaw0 z;LT945Ev3IkhP6$lQBiMgr+vAL}{8xO&IObqJBEP4Y^x&V?iGC=1lVIbH^Z!eXxr@ zz)D7Fon`z~N|Pq>Bsue&_T9d;G+d8#@k^cq~F^I8ETsZ*cGOf*gZ4ghlAzW|aZ;WA13^B!Tlr0sWA zosgXD-%zvO-*GLU@hVV(bbQ`s@f~Ux=4}(@7O)%o5EH((gYflccBC@jbLF3IgPozv zglX2IL}kL1rtn4mu~`J(MMY83Rz6gc1}cX4RB+tZO2~;3FI# z@dU(xa5J_KvL0)oSkvwz9|!QcEA$jKR@a-4^SU3O449TrO+x$1fkBU<<=E_IHnF6> zPmZ7I2E+9A_>j6og$>Nih~b2F_^@6ef|Hm-K2(>`6ag{Vpd`g35n`yW|Jme78-cSy z2Jz7V#5=~u#0eLSh3U4uM3Smk31>xEh^-Os%&5tK6hSAX83jJi%5l!MmL4E?=FerNG#3lj^;-F1VISY!4E)__J~gY zP{o~Xo!8DW{5lsBFKL~OJiQoH>yBZ+b^};UL&UUs!Hbu7Gsf<9sLAsOPD4?-3CP{Q zIDu8jLk6(U3VQPyTP{Esf)1-trW5Mi#zfpgoc-!H>F$J#8uDRwDwOaohB(_I%SuHg zGP)11((V9rRAG>80NrW}d`=G(Kh>nzPa1M?sP;UNfGQaOMG1@_D0EMIWhIn#$u2_$ zlG-ED(PU+v<1Dd?q-O#bsA)LwrwL>q#_&75H)_X4sJK{n%SGvVsWH7@1QZqq|LM`l zDhX8m%Pe5`p1qR{^wuQ&>A+{{KWhXs<4RD< z=qU6)+btESL>kZWH8w}Q%=>NJTj=b%SKV3q%jSW>r*Qv1j$bX>}sQ%KO7Il zm?7>4%Q6Nk!2^z})Kchu%6lv-7i=rS26q7)-02q?2$yNt7Y={z<^<+wy6ja-_X6P4 zoqZ1PW#`qSqD4qH&UR57+z0-hm1lRO2-*(xN-42|%wl2i^h8I{d8lS+b=v9_>2C2> zz(-(%#s*fpe18pFi+EIHHeQvxJT*^HFj2QyP0cHJw?Kg+hC?21K&4>=jmwcu-dOqEs{%c+yaQ z2z6rB>nPdwuUR*j{BvM-)_XMd^S1U|6kOQ$rR`lHO3z~*QZ71(y(42g`csRZ1M@K7 zGeZ27hWA%v`&zQExDnc@cm9?ZO?$?0mWaO7E(Js|3_MAlXFB$^4#Zpo;x~xOEbay( zq=N;ZD9RVV7`dZNzz+p@YqH@dW*ij8g053Cbd=Mo!Ad8*L<5m1c4Kk ziuca5CyQ05z7gOMecqu!vU=y93p+$+;m=;s-(45taf_P(2%vER<8q3}actBuhfk)( zf7nccmO{8zL?N5oynmJM4T?8E))e;;+HfHZHr` zdK}~!JG}R#5Bk%M5FlTSPv}Eb9qs1r0ZH{tSk@I{KB|$|16@&`0h3m7S+)$k*3QbQ zasW2`9>hwc)dVNgx46{Io zZ}aJHHNf1?!K|P;>g7(>TefcLJk%!vM`gH8V3!b= z>YS+)1nw9U(G&;7;PV4eIl{=6DT^Vw<2Elnox;u@xF5ad*9Fo|yKgq<>*?C$jaG2j z|29>K)fI^U!v?55+kQ*d2#3}*libC4>Dl4 zIo3Jvsk?)edMnpH<|*l<*0Pf{2#KedIt>~-QiB{4+KEpSjUAYOhGDpn3H_N9$lxaP ztZwagSRY~x@81bqe^3fb;|_A7{FmMBvwHN*Xu006qKo{1i!RbN__2q!Q*A;U*g-Mz zg)-3FZ`VJdognZ~WrWW^2J$ArQAr1&jl~kWhn+osG5wAlE5W&V%GI{8iMQ!5lmV~# zeb3SKZ@?7p;?7{uviY6`Oz16t0=B70`im=`D@xJa16j2eHoCtElU*~7={YUzN41sE z#Th>DvJq-#UwEpJGKx;;wfDhShgO0cM|e!Ej){RX#~>a?)c2|7Hjhh2d=)VUVJL<^Aq|>_df4DX>b9W2$_DM zTjF#j(9?Co`yor?pK<16@{h#F&F8~1PG|qQNZPX^b!L*L&?PH#W8za0c~v6I2W($Jderl%4gufl z#s;C*7APQJP46xHqw;mUyKp3}W^hjJ-Dj>h%`^XS7WAab^C^aRu1?*vh-k2df&y9E z=0p*sn0<83UL4w30FqnZ0EvXCBIMVSY9Zf?H1%IrwQybOvn~4*NKYubcyVkBZ4F$z zkqcP*S>k6!_MiTKIdGlG+pfw>o{ni`;Z7pup#g z4tDx3Kl$)-msHd1r(YpVz7`VW=fx9{ zP}U8rJ-IP)m}~5t&0Y$~Quyjflm!-eXC?_LMGCkZtNDZf0?w<{f^zp&@U@sQxcPOZ zBbfQTFDWL_>HytC*QQG_=K7ZRbL!`q{m8IjE0cz(t`V0Ee}v!C74^!Fy~-~?@}rdn zABORRmgOLz8{r!anhFgghZc>0l7EpqWKU|tG$`VM=141@!EQ$=@Zmjc zTs`)!A&yNGY6WfKa?)h>zHn!)=Jd73@T^(m_j|Z;f?avJ{EOr~O~Q2gox6dkyY@%M zBU+#=T?P8tvGG|D5JTR}XXwjgbH(uwnW%W?9<-OQU9|6H{09v#+jmnxwaQ-V;q{v% zA8srmJX7Fn@7mr*ZQ@)haPjWVN@e3K z_`+@X$k*ocx*uF^_mTqJpwpuhBX~CSu=zPE(Sy%fYz&lzZmz3xo4~-xBBvU0Ao?;I-81*Z%8Do+*}pqg>bt^{w-`V6Sj>{Znj+ z70GS2evXinf|S#9=NNoXoS;$BTW*G0!xuTSZUY45yPE+~*&a-XC+3_YPqhd*&aQ>f z$oMUq^jjA;x#?iJKrpAqa<2<21h*_lx9a}VMib;a6c$~=PJOj6XJXJ|+rc7O7PEN5uE7!4n9nllo@BI4$VW2Nf_jqnkz%cvU4O4umV z#n6oXGWOt3tuIjmX*b!!$t~94@a@QgybLpQo3icAyU`iNbY~XNAArFAn$nFJ()d-U zFaO#nxxVF-%J{UB**uRo0*+?S>=^il)1m7v-u`PDy*ln%|3E-{3U~R=QcE&zhiG_c zDnGMgf1}3h1gWz8IV0Oc7FmEt>6W?Eva;J`(!;IIny}PvD?vztz`F6su_tUO`M%K5 z%C#=nXbX})#uE!zcq2mB;hPUVU1!`9^2K303XfOIVS{mlnMqJyt}FV=$&fgoquO+N zU6!gWoL%3N1kyrhd^3!u>?l6|cIl*t4$Z$=ihyzD7FFY~U~{RaZmfyO4+$kC7+m zo+-*f-VwpUjTi_Idyl~efx)!$GpE!h+in4G1WQkoUr<#2BtxLNn*2A>a-2BL#z%QO@w0v^{s=`*I6=ew2nUj1=mvi%^U@2#Wf& zs1@q6l8WqrqGm!)Yr|*``||#A+4#du6`mR^_#?CymIr}O!8Zm?(XY$u-RGH;?HFMGIEYVuA1& z`3RlG_y0%Mo5w@-_W$E&#>g6j5|y1)2$hg(6k<{&NsACgQQ0c8&8Tdth-{@srKE*I zAW64%AvJJ+Z-|I~8`+eWv&+k8vhdJk5%jolc%e`^%_vul0~U8t)>=bU&^ z6qXW&GDP%~1{L1-nKK>IsFgDJrh>!wr3?Vu-cmi#wn`;F`$GNc_>D|>RSuC8Vh21N z|G;J1%1YxwLZDD400Ggw+FirsoXVWYtOwg-srm}6woBb!8@OIc`P$!?kH>E55zbMB z8rdpODYfVmf>cF`1;>9N>Fl(Rov!pm=okW>I(GNJoNZ6jfIunKna-h6zXZPoZ9E2PythpyYk3HRN%xhq2c?gT$?4}Ybl42kip$QiA+ab zf-!EqBXkT1OLW>C4;|irG4sMfh;hYVSD_t6!MISn-IW)w#8kgY0cI>A`yl?j@x)hc z=wMU^=%71lcELG|Q-og8R{RC9cZ%6f7a#815zaPmyWPN*LS3co#vcvJ%G+>a3sYE`9Xc&ucfU0bB}c_3*W#V7btcG|iC>LctSZUfMOK zlIUt>NBmx6Ed}w_WQARG+9fLiRjS1;g49srN1Xi&DRd|r+zz*OPLWOu>M?V>@!i49 zPLZ3Q(99%(t|l%5=+9=t$slX0Pq(K@S`^n|MKTZL_Sj+DUZY?GU8sG=*6xu)k5V3v zd-flrufs*;j-rU9;qM zyJMlz(uBh0IkV<(HkUxJ747~|gDR6xFu?QvXn`Kr|IWY-Y!UsDCEqsE#Jp*RQpnc# z8y3RX%c2lY9D*aL!VS`xgQ^u0rvl#61yjg03CBER7-#t7Z++5h_4pw{ZZ~j0n_S_g zR=eVrlZDiH4y2}EZMq2(0#uU|XHnU!+}(H*l~J&)BUDN~&$ju@&a=s$tH5L`_wLeB z944k;)JIH^T9GEFlXiNJ6JRymqtLGZc?#Mqk2XIWMuGIt#z#*kJtnk+uS;Gp}zp$(O%LOC|U4ibw%ce-6>id$j5^y?wv zp1At~Sp7Fp_z24oIbOREU!Mji-M;a|15$#ZnBpa^h+HS&4TCU-ul0{^n1aPzkSi1i zuGcMSC@(3Ac6tdQ&TkMI|5n7(6P4(qUTCr)vt5F&iIj9_%tlb|fQ{DyVu!X(gn<3c zCN6?RwFjgCJ2EfV&6mjcfgKQ^rpUedLTsEu8z7=q;WsYb>)E}8qeLhxjhj9K**-Ti z9Z2A=gg+}6%r9HXF!Z~du|jPz&{zgWHpcE+j@p0WhyHpkA6`@q{wXl6g6rL5Z|j~G zbBS~X7QXr3Pq0$@mUH1Snk^1WJ0Fx2nTyCGkWKok$bJZV0*W?kjT|mkUpK<)_!_K^OoTjMc+CWc^~{ZP8vgm`f&=ppzKtw}cxwV^gppu}^df1|va7Q?@=(076-( z4KJVmu?l(aQwmQ*y_mke>YLW^^Rsj@diLY$uUBHL3yGMwNwb7OR3VD%%4tDW(nC984jBWCd90yY(GEdE8s(j>(uPfknLwh!i6*LX}@vvrRCG`c?EdB8uYU zqgsI4=akCeC+&iMNpVu56Fj2xZQHs6SdWssIF#Q@u@f9kab0&y*PlG+PynjHy`}GT zg%aTjRs2+7CknhTQKI%YZhFq1quSM{u24Oy2As@4g(bpbi%y1i0^TwI)%1Whpa~qE zX4MD(PgFEK@jZBPXkFd437aL6#COs$WrNT#U=er-X1FX{{v9!0AS$HR{!_u;zldwY zKko!`w2u@($c&k_3uLFE0Z*2vms?uw1A{AqZw^jwg$|D7jAY20j`s*l##=4Ne_K5) zOtu6_kziEF@vPsS7+@UwqOW6>OUwF$j{r4=nOSf-{UC(rEKidie7IUn>5`UoNJ9k) zxJXXEBQifng+Pte3mPQ76pVlZ<`jnI##F1*YFA*)ZCEncvgF-%)0dUXV*pXTT^L`n zL=?A5Vty#{R9W4K)m$`me~*_(&a88M?Eon$P-YdVG}#Gq4=hh#w=`>8f`9}}zhv;~ za?I=Gb3v$Ln?-SDTBow0J5Tt&xPlw|%`*VTyVee1Oh<-&;mA|;$ zoPl;^f7Q~}km#_#HT2|!;LEqORn%~KJaM)r#x_{PstSGOiZ!zX2c}^!ea3+HSWrwE z=6SJ!7sNDPdbVr#vnUf}hr&g@7_Yj&=sY=q(v^BwLKQm|oSB}172GpPlj?a3GqX#B zJko4zRRttIY>Fv#2b#A<_DLx=T@eUj+f}!u?p)hmN)u4(Jp(`9j58ze{&~rV?WVbP z%A=|J96mQjtD037%>=yk3lkF5EOIYwcE;uQ5J6wRfI^P3{9U$(b>BlcJF$2O;>-{+a1l4;FSlb z_LRpoy$L%S<&ATf#SE z;L?-lQlUDX_s&jz;Q1Lr@5>p_RPPReGnBNxgpD!5R#3)#thAI3ufgc^L)u%Rr+Hlb zT(pLDt%wP7<%z(utq=l%1M78jveI@T$dF#su(&>JkE(#=f4;D54l*%(-^(nfbCUQe)FV9non9F%K+KZ(4_`uOciy82CO)OolxisUd0m^cqueIRnY< z;BgA4S1&XC3uUP?U$}4o&r|0VCC7fkuMZBa|2n4asR>*5`zBaOJPWT$bNn(W_CK%L$c2AsfSlwq?A8Q6 zhK&USSV=^-4vZ^5<}pnAOb&IKseHNxv_!|B{g@d^&w%{?x;i3iSo)+vt^VnMmS!v) zM)W)05vXqzH5^hOWWw~$#&7HoIw}}DD3bCQgc=I8Rv|G5fM8O^58?--_-*>%Nwk)j zIfvfok0n05!w%tZ=-dpffezI7(+}yX5XhwYk#0@KW%PkR;%#t|P6Ze_K*N6ns%jOt zNeW(bRsv0BK7ah~9U~UBAVA_L34F+;14x6-;I|o=%>?sS3@dpRv|GKxilsa#7N#@! z!RX~>&JX&r{A^^>S~n_hPKkPR_(~~g>SuPj5Kx6VI%8BOa(Iit&xSMU8B#EY-Wr?9 zOaRPw0PEbVSW@Wk{8kkVn34;D1pV2mUXnXWp{V-M9+d}|qfb6F`!a9JQO_-wlH?zf z4Sn0F4-q-tzkaJ?1fV0+cJBF$f0g6*DL6U3y`Tr`1wzCiwY#muw7Q-Ki)uN}{MoCWP%tQ@~J4}tyr1^_bV9PScNKQHK=BZFV!`0gRe?mVxhcA4hW5?p0B<5oK+?vG^NM%B%NDOvu0FMq#)u&zt_-g&2 z7?z%~p&32OAUSQV{<=pc_j2^<;)`8$zxCEomh=rvMiliShS?ahdYI1grE-M&+qkK_ zD=5Hexi<&8qb4hgtgj81OD(tfX3EJSqy9KFcxpeBerG`apI4!#93xpEFT??vLt>kf zac28;86CpMu=BWIe$NOT~+Es!y#+$ zvm2s*c`J9Gy*ERvLSI<9<=j*O=0xUG>7rYh^R4bGsvz;j-SBO|P^OQ1>G9_akF}D; zlRmB@k3c5!s|Vz3OMZ8M*n0AMTiSt5ZpRy+R1|ckna&w`UQjklt9f&0Z~=->XImVA zLXizO2h=<|wM~w>%}3q1!E{oSq7LBPwQ~93p-peDq-W?wCm8NOKgTSz-P)|cm}S5&HBsx#C@Ba5;hzi#Yw@y-kC~)@u4}Rf?KV0$lPjv}} zcFpNy=YJfsS||9&!-JFjw=@NU96ESzU^gme0_oNy?})II`>Sy>bUCHs_(m&)vn^&isCl+`F~qu8elAO z)-ZP7`gYE2H(1)5tKalz&NJbcutAU&&JFV~$Jrai31^j>vZ|HV1f}#C1<5>F8 zS1RWIzM%b{@2dAF^$+i4p>TC8-weiLAPN+Aa#(bxXo9%Vz2NEkgF&s#_>V?YPye^_ z`` z-h3Cv^m6K%28I$e2i=cFdhZN?JTWhqJC{Q9mg0Vg|FiPEWDl&K)_;Bz_K`jH7W7QX^d$WQF*iF@#4_P*D36w9&iJr2E{w?LRFapwZIIVHGH ziTp*5>T{=;(E}z{1VL4;_H`BAXA~&zpeWX!gN9m|AfcJ{`!XVz48O^&+0Gd|w;udP zzU|DbGTS|7qZoEoDZEH9Kb0%DZvCaWDzuJ=8jZz}pqPn+I!c_+*~>m>BQqN2560*< z$6sx_y8WRqj$SugYGip+et$;iJ!SQAx=HgVSh_3e)MOFHuXD@sg>Yi_p8Sh`{lP=5 zo?AFv1h;KqR`Yj!8Pjji3lr+qae2|a1GmlxE*su%_V)K0Xu0(#2LcO!*k11w*V12$ z;f~i{kI#9PzvFLZ3pz@d558HeK2BTvk*JvS^J8L^_?q4q z);;4Z!DsV!P*M>F>FiF*{|p_nUgy;pDh?J8vwO;emgOAAcxrgDXiSDS5ag?0l*jj< z(khZ3-)>eiwPwpb6T9meeL)!2C-K@z9fF`0j|t@;^f5+dx86R3ZM{bnx9Hm1O$s)N zk$OvZR0u2`Z^QP8V%{8sEhW~_xbZMad2jtz&0+ekxmp;9`ae;_f%-ltk5E%)VT*a6 zRbMnpCLPnalu+1TafJ4M0xNV8g}U4Mjk{le6MA|0y0rk)is}M%Z9tUU22SvIAh7`w zTysd{Pztfkk=jD^*!lA+rBcqb)Fx`A5iaU2tl&XdL1D)U@pLEXdu%#YB*ol1N?4ti zHBQcU#_%UqiQ1)J^u-ovU@-7l?`YzYFvA2#tM0mEh3?CpyEh_NUuVajD16t zyg$C*5du9R=K~6mCJ`W+dFI$9WZZauO)p2H)*SKpHVsIu2CxfJvi2>; zcit#57RP7DpSwMF-VBm|4V5d=tRgX7RM9%KQ0JRo6d<)RmiIPWe2zh6tmswP`fs^) zwy};#jk|NXMqCSfwIR3QZ#W2`(%sJ>qvk=53CYoLmQt9q|2Gm$sB;rEuBqGJA1OUM zoyl4Wy-HYn0J6L=cad8o)R!Ea^;`rSMg9hYo3?Fw6B9dUq75a-MSb56n8~AAsS(JP zZ!1khPu}!GRpsj+jvl`N1tDD8m1myJCI3c-c<9U-1Vg`xJO~}5_wvPXYh^=Boo^|V z3Tp}|lH!9m4Ipa_$p;b8fjUd=zc4iO7vr)M&Xs0_m$fgY@+hB9%K~4*9$p0d)m2bO ze5JH`W0fnIKdcW!oO#^g1YceSQ4u->{>u@>tLi!fky)o&$h(=he?Fe_6?}O~iSf(F zV&(P~*5h>BW{3e1H%8*7#_%L1#>W97b0@jHtliES^w6w5oldI7QL+?I(Pl$DaN>~d5nXx z;CO1E+S?3E2PLq~)-?ygkHAO1m&hOYmj7?;2XM!$D^f0l9K4P{n}mgb{CoYH6RJ8o ztydc6dNqA)`CG?=Gd~EIbi`UM)eyzGF^+i?&TOdyW~mFH_^Gye(D}clDVFQ@V2Tvy z7rQIaq8Xx`kC;AO-_{k%VI2e6X@bIy^mupEX%{u0=KDUGu~r6lS*7GOeppy{&I&Ly zjOTz=9~jC|qWXznRbrfjg!1`cE!Hzyjzw6l{%>X)TK(UEGi9Uy3f9D6bbn0gT-s`< z8%$Msh!^8WidX7S;)n2jh_n1-QCtSyOAKcPQc(Xlf0*Q|5CSBjo(I-u!R0GJgzTkL z|6QdQRrUMbUO|q0dQ%+d^4)*Mjbm$R}RUcz(7|E0Bq-bAYY@)OsM<+2>}CV zzPBgeD~kBHE(Y+@l2orJrdtV7XXq_V8IETas%7OCYo`oi)+h&v#YN!Qpp7drXFS>6 z?r-q7px+(rIy+bo1uU#I2A5s@ASe01FgGMbouFkhbkm-9yZ8Q2@Q1vuhDQ3D3L+zA z(uz8^rc24VmE5r0Gbd;yOrXnQKAEBfa3@T7fcF$#QYv^00)VZPYehpSc@?^8we}o{ zlX0~o_I<`xSfI8xF(WXO-DX1>wJ`XN?4rw@}_RLD*${$}UaXL=oM(=SDMIxZj1Ji#jAcrH7nYG`r z#ewodj>F5Bf9j(j`a;>)=*2j_ZN}vf!~Hq`2Eyt;9UH1_(yjq1OUO(1M0lI3FZ2j-fU9)L59v&OiQ>5$;d!jg?Fo{Svf5t5FCZbb?)* zJN=Q!?2BztV$7)CWtG0MO~Lr4E5>aoHD5N4(+@~gQEbZTc4s3HrIl_G23PCng4Y3f zbLZK1A-x9x!)WwuI=UBkQ5QyE^&Nrw?@fsRKK41G9-xq=#VyO%CEo`{_eioDj%M!3x=>I zfOPFiFX{1t-|+3E@?UuK=0miGN04hW0=JnJrEyWw{Bg-jMvAA}cg<5LN1c5BQdrIZ z#+bxj9Jbu`11@IUjU|RKfL(UzRlVB4XT ze|(WaxL$KiRqkgCr3^Al(19!_Y7b=E(4Xm7LCO$y5+k;Fu6B#=OSzW`-7p{zRv-_) zPr!|km?8aF}+3hm)QG92YaI+jctX&5IrvTUGf{Y$)TK6)s9v!SMhU=HIpEC~2 z4>o14mG$El2sTA(Ct?xS!l*x7^)oo}|3+BF8QNe;bBHcqdHVmb?#cbS*NqZ%mYS~z z`KLoq7B#KULt%9a#DE%VTEo4TV03T2nr`FK5jUTA$FP0JH6F9oD*|0z1Yf2b5?H0_ zD|K|_5Zk`uu?ZN0U! z_mL>>F;mnHU=@to!Vv*s4;TQr9y)L@1BXXz^a85NSifPTL4h6I>+m_S3~FkXB{N?E zS<3ue_(wqaIS5;4e9{HB`Okl9Y}iFiju+oTqb)BY)QT?~3Oag7nGu-NB5VCOFsiRs zs@m%Ruwl^FuJ1b}g^=*_R?=SYJQ@7o>c9j>)1HgB zyN9LI9ifwu{Shlb6QO2#MWhxq~IG!U^I!6%5}(sbi>=bq8!8@s;4Iaun#kvh7NPwX34Rjbp2f!D)cF&sNIO%9~;C`cs&ZY2=d@c3PpN$YZjUT}X7rY`dlWX$yc znw(7=fzWapI=KzQnJ(6!o0K_aDk!^dZ#)pSTif+jQtQXga$bPApM z=);jZ5c*?*GoeGMnV0=RrZucRRYBjx>tx`A3OuY)#tp2w7mh}&kj)SKoAvbbf;uO! z?+RItUow0xc*6StuO4D--+qY!o}Isy}s;ts5aM5X~eJUZoLOq@dGv=a4hHJD<* z5q{dZSN{bv_(Vj#pFm7Q<$C;MwL|Qizm~QCFx~xQyJoCOZ$`sYD}}q>PwRZjb<=E< zAeMP?qVfM>xu2}Il2xT6={KBdDIstxY-`5IWXN zUiWV&Oiy5R_=2X9Y$ug9Ee=ZSCaza!>dWBMYWrq7uqp>25`btLn^@ydwz?+v?-?2V z?yVwD=rAO!JEABUU1hQ|cY+_OZ14Hb-Ef`qemxp+ZSK?Z;r!gDkJ}&ayJBx+7>#~^ zTm<>LzxR^t-P;1x3$h;-xzQgveY$^C28?jNM6@8$uJiY81sCwNi~+F=78qJZ@bIsz1CO! zgtPM~p6kaCR~-M>zpRCpQI}kUfaiZS`ez6%P6%*!$YCfF=sn}dg!593GFRw>OV2nQ ztTF6uB&}1J`r>gJuBP(z%KW{I^Uz%(^r5#$SK~%w1agl)Gg9Zy9fSK0kyLE24Z(34 zYtihZMQO^*=eY=<5R6LztHaB1AcuIrXoFuQ=7&C}L{c?Z$rto$%n=!whqoqG>#vvC z2%J5LVkU%Ta8hoM($p1WqN}wurA!d@#mQGU5Nb>~#XC84EYH)Zf&DZR!uY+-;VqS< z@q?$ggdX#auS#%%%oS^EN)?JhSR4JYpSgGRQZD<9!YvvF+zp0>C#$!x*x}l8U|Bb& zv?v*im5Bq_(5Wi40b1^nKun$XTST(a8yOAcqQZmKTgGLo)Ig6JuEh5J9NnqJXin@Gxzz-k6xXWYJ&@=JZw=$+ zFPGde%HsR`gI+y`rtiPaMYwbtyp!sVb!pX~;c3zLoPO0eaZSV+O_z z%9H@UhqNowzBTPcMfL6kC>LRaFF6KVaSv1R@%4}rtleX!EMnL`rethYrhTLj1x$tj z;)H!fKo08&T(;i|FT&rPgZ*D0d=B2dXuO_(Uaoi9+vEhs4%{AD{Fl@4^|`X=PvH(s zI7$6bWJiWndP$;&!kSCIR1l57F2?yzmZm~lA5%JKVb;1rQwj*O=^WW~`+n*+fQkK0 zydInOU1Be2`jhA!rnk1iRWR=1SOZpzFoU5{OPpc&A#j6Oc?D&>fAw=>x@H7?SN;d^ z-o&}WR;E|OR`QKItu(y4mT)%Pgqju-3uyH?Y@5>oSLO2Y(0(P!?_xOL=@5+R7rWw# z3J8%Hb@%Pzf^`=J6fEJ_aG6+e7>OUnhaO1(R1<6>f}L z?d@Wnqw9?^;2?q(b@?Wd=T6r_8a@Z4)*_@Q7A`+ zW3w?j!HW0KbhxF%D`9d2HpvIrBxM!36W3Yh5=8_0qYfnHm*yiLB?Ay|V10N%F9XYq zanaDtDk$rS+|_H_r|a${C}C7b{E)Ii20-a?Grff$E?&|gWF<#Ern2GqhCiS0~Y%knIi8zY^lE4qLaR-3M;_Rkz(s;wu z9207W1PXIe#4h4Zw}dvdV&FYcnUlD5_C4hzJ@bPSBVBLpl$&52mi+wwH;svyVIzAB zoA+NQ;Hpqh?A}^Et~xhl>YQNQwh20!muW{ zq}|Pg3jHZWnDBN?r1KhiVG$%Sm-4+=Q2MZzlNr3{#Abqb9j}KK%sHZj{Vr2y4~GIQ zA3Mz1DjQ3q(CC~OyCaZn0M2!){)S!!L~t>-wA&%01?-*H5?nzW?LJB`{r&)vLB4!K zrSm({8SeZ0w(bL9%ZZAZ*^jf=8mAjK^ZR0q9004|3%73z#`-Npqx*X^Ozbja!C1MW z-M~84#=rU1r>p{+h9JU<#K_x$eWqJ+aP%e?7KTSK&1>dlxwhQmkr69uG~0iD@y|L- zlY0vSR2|IhZoS6PpfUai_AhKo2HfdD&mhv#k51CX;T z*sU)XbDyfKjxYC$*_^(U)2-c0>GJ(zVm$CihHKlFSw&1A$mq$vsRt-!$jJe3GTaZ6 z3GcVvmwZ0D>`U+f3i*pQ>${p1UeyF~G9g~g-n{ThVOuC#9=ok`Zgz@qKCSN!1&P`N z=pdlGNwal%9;)ujwWH*#K6CQG*fJDAQiKlO2vKJHeA1lj&WQC+VU^@ea8$#~UOX$*Q!V^8L- zL0$W5(Y3=??%&j_WUq6*x>=?BfmI*d8fmDF*-!XVvxL8p7$r+}Igd_(&`|D*;Z#GE zqm{tHx&aHBpXw&~l6>7-FlyiSPJtTJblAjLU5Ho$FeN0mDguFAq?r+6^~o6|b+rfE zGVcZ&O-X~tE3liGcdI~hHSCT+&F&uH8rr&f{6pr^1y5061`fu~=^_|Idrgti5+*U7 zQOb9G?Rz$j-G0Y}x+i{HB0!4ZmKzykB<0;Rbmo2)T4|VdcwujI_otLG@@8OOKg3kw zP|0ST0D4@zT?O=(0Pikp)Rpwxw_VsmW4!^j^sFd6r5l zw}SG_HQPs>ae%Bq{sye_SaBX%|F-}&^)Wz@Xi<)YNbO?lPs7z@3c;$b^Aw@>E%mOj zW^c%IdtC(Kk@s*}9NbKxEf8SZtP+32ZTxjnrNWS7;W&D~ft{QY?oqOmxlV7JP!kW!Yj`Ur{QbbM1h=0KMaIAmWiISb7TKd4=gMeo+Tcz2>e#NihnOV%iNdx` zeiuoOK^{}D+M+p(Y7EC=&-`$B0F< zQ=zHaM;&QQR4jM$sG=N&sqOvD_Bx*drQ6c@u0()g05cwl`Xm{!S_Nuaa2KlL*rmmk z51yPE)q?Bl$sNM474Y!=zZ zc{EVGpdJ!Su{Qq%llR5O6#zK8l(ld*UVl87@|iaH@C3+*;XBxjEg&fsQrzpMo3EEG zv*Tpms7a;7!|iz8WY7={0a$0ItO-(ajXl;wX_$$yzEF5k9nc>L3wv!p{8h2)G0W?h z{v6vH=7+>$Ho^+)9hDtCd+S_yh8pzS9$)hYev-=eDu?lGIR;-fgz+dr+wcmM-^dZp z9}`&kAf$~z1ovF)>Hgxc!Xe3cju-jQRluCm;c_1=PYQygb?Oxe z!QG0L3sT_k=WpfOPL#|EPlD^t;ENCC39O?tHd<(kfx7SOcxl+E#;ff19_+{vbkZSvbS$I{#>31KZj^$n%ayX0jj}EvsgnHg16P z_A6Y)pdp>kLW<;PtR*Vs#mVb%)ao7AXw{O&hBDmD;?mc3iMH;Ac@rZZ_BQa8CQ~|0 z&d1L{in-z--lBO|pxqc%bqy^~LAGv=E*eaVU~OeuVV{d`Vv#-_W7EYdTDzVraG9H+LC_dWcgZMn~KcP)XvKWbcr5&d+=a>{*(Ha6Y1$==bR z{O-?$7H;`2dt0B%Vm?6`_?ZOjJkyu9ZJsh^WH*+es&^@KDcR%Zej%3PJ*XovgyhTbaH(!H1H_OF~=*f55Jr8A%uW zz5IoAB~1e2-tDGp9}`MnavAMy?jgPM5F%y`%$}dFLrz_* zIrO=afT8+AkK5B1s3{ZDVP$g6y$-*U*=?-fh!cNyn3q6YhNhfRxW&GLIJ2#>9bYMD7-F%{|Iw%@a=DoAAU;3k9p$`V zImKm{5HU~wq|nQFwab)_7lNckW#1z2$|oW5x7vDbBURVjw8674P?L1ogMKpHoV>;# zO%*1OwI|($UOr#hL(*M~qsn3PF%_|15uc%Hy9@D>_~N|?<%lig6yKX0a#1s$o(^Laj8bF#5fGPOFMGmMiUaxSwE}Qf#SG_f79d2Iv=TFBXzTpr$^avJ?=|arh2<+ce}&248Kw0} zhlva`wD6X~s7|37la4FnFOgIHhBiFo`lw~?lSbk{>)P(3jyVhM4O)a=GX3(sW1vIC zz0mJ>;J{!eN5#nf2>$u=3Kq>`7u9QnChi8>CjONBN-b+W_UQIuN#{N$Q<$}IOvpQP zB&5ZrY{V&D=4)voh;6<1U`PFA>V%XUW73S9D^J>cQYfzIyIV5i35WNb5K9c^|M}=* zN_C3rnjCZP1^v{;EaGK7Tp5z~B#?f5NZaAsFUOLK)mI~bJTaL8DF_eRikE{%^J?y9-n_U32EKHPCkB^ZN2*zk{bC=GM%_I z61}nkr+Plg6S0V=mY>H_KQU&)P~=y3$#$*U8FunXkb_e1O-7t@m$5re%u!_G%^?_| zRIJzg+lX$}+ba|qx)Ec6c^ip;`_QfQrD~SPa4MoyRUOtX&~^XWcO^a}KBkXK9J{ZFOA~rovYa0!7btTC*=xNQrwJ)$Eu`TT$;%V&2@y@$ISdNn ztbM7|nO+U9r;ae{{;QiNEYpe4nrFq_x3 z4Tvf^b(I@_3odwhVe!aC0X&~inrYFu# zh)+eF__8ly&nLr4KlLWl%B_ZMo=zCH2QfO^$lJ zBvU*LQ#M(5HQ}2Z9_^y~i@C#h)1C*?N3v68pY+7DD09nxowdG#_AAM5z&*|-9NcB{ z_xKUY>Ya7>TO#Bat}yM}o(~8Ck^!QHnIj8N9}c*uyIs}IEqGn`xP;q3vhW6gsqUe>`m1 z)~ad@y1=?H`1SNl?ANCs5ZD`8tG&Hi=j|R%pP(%gB8pd)Q--E?hWU@)e?>SLV4s(- z!_I^oVC0x97@I(;cnEm$ttKBnI3gXE>>`K?vAq~SK?0YSBsx{@s1ZdiKfFb|zf}ju z7@rJb3mC{U`$R`YS(Z#KyxQx_*nU`kf;}QL%bw17%5~6!mMao^-{FFmX}|ItFuR~F zAAvTF%f4XKYo>2-PJ~ro@Ly#t@Sf69CrA+rmMRpihqH7V&SXX+$Sw`HZF`I*_3Vjz z%kPMyN0J3sl>X{-h12)j&XRhAAI;Aou%%z}gI>G+32z*qpZg{m`CezFrzg#&yc<1` z%j~}PN!F5Ddq(>R{+t0v{j6v^0XwWGu@5+`-$m`_>pCzM`r}wz*8Qv=$|P0R$%tJp z>D+N4GZ|Tg>XL<6XP9_wQRGDs^1icY*5GP4>*7mGMr;V zI%kT_^_SQml6$#uRE4Ps>}?ES)_XI8m-%GN{o^itb^S7e_bM$-wo_Ws)W? zx4_6#*X;T$n2N==N0#xzb~BQU#%^NF6|~898JGDbQxjK(ex;Q}_Qn@?Y>!kkUYUeY z&VclG1#eDPU78K@^p3tAUvZi1(nFfk6AAVHWt)Wbi7dPbjA4isOY~?*1&asp!wg#Q zSpSI6*!TGn3|-%vuJE<9V_1EKkz_0%z}Mb7;E!uz)+0^k;@x+<5tzj5 z!InbRtc`YwNCbCac{plY&Y}hWp#PC{o@5UsBj#tv3f^ns^`;$MVN?>q!pW+MYeC7= zkWr1kAX(0xVQ<{qny&CO*|g1{Mk_yE>1t}_YT<5#p8P7QXf;o|s>XQ#SoA&!ddE+8 zOM&VsxsRGS(Spli?P$^pK7Ty{v86RP_6h|MU^J z`J>vn0|BG3Vf!uR0zM|GwtiTPZNb;a@@1+V5+$P4GI_&$%6m!YRGL=lz5kh?z#5f55 z76COi1`R(5p69;ThuQnJ$R3w?I?jigai2arApagd=^tT~oMUWp^u|H_@zXBjpI)Dv zEFc^_`mVu5U*;ClT?x-t9{#fto_+92GF^dotz0sFWTDwZ`s40AY@mv+Qh5c-Ts8Zp z!(v7!zPvFhUZ-xkR!IvaW`{PqN|k)L4*anbtmK+UU&K*awl?DhxRalbtmDw`$#VzK zYFaG}?$F)1j`Qx7wbn|XzMJ&g@3Ai#u5M?%CLPghk;lD^)-|21{Sr+M(suBU4}6CMTMxc_tD;X;z<1-{FeHte=kh1B9O6Hl z!v2i$d1VFC&z&58zU0`G#7^K3Cs@9LYN16O%Vz)?-iQL!G6&sg6aaX>DBZmm@lFrRJpcL{K3(;+`$9GDFDw62Mud@LZjabzVC=w$dx>TQa}U z-{dhKYTYx*C=Fio`ez@wrzx+p%Fk3i&v?6ENXMb3p^?;_&huLLueDwr zpRqHbU%i;9TmexFxCS8F1rPo-ea3!}!ew7{(($76Rdnfa`~$9{8H@f7U&0&HjZ3TZ zuBc||%FljS_e&wNZ$1ezT$*})XAfm??$_cY_?13vM^tT0EKY2ptb+v5P10}a%aTk_ zh8@_T{ns2@jTFhv`)-Vxh}u(0DiL0MUi(We_eic$;gCoqj(T_S{jDo^PahnKJUp3@ zMOk+%weP*c%K6VFXR2icY`J~-&fVMYUg6fsFI->jlA|9`+07y~$Fsz}^;w;mNk$ms zu?y)VA@QH__tvYDudhEWuDD20H&uvrf_boY{($?5{s-SDjyRxSC%%2Xs5d2dpjdk$ zU*NURD#ovwIfd^H{fXR@UuaooJtQr7$d0+(K+1UEwtG9_T?sb$ExV$e-bpf}a@YUe zuzInI59w!x;<)>Be;a7ukLW>V=8~J6nKU<0@H+SQ!Be;1Za_pw#hiuW_PMPBo8W2G z*WDtiIAN<>HQOmh)DMi{s-0H^GmV3QMf4Zu(zXT!-c;2)uv4gUwt(-}-N*|KUOo$h z+Ak^R)h8yB5UD8 zsSjHgY}KguNi?xV=tdCWqJR!~dDpFQoRJOwxrWH^vfRq4%)v;sDfIjsLXF^)uy>!i z*S8Njd7yfa`+7(|8H9j73Rh|TwFpF(8H-p;RLLIU>k<*qI%A*SL{u$%<=X@Jm1QFe zVkQ(X8P4Tohl?_tSO__^aqaI?k$CC8uNLv2mp_zD@4oDaZfEN5;3#XY!L{8B!;Dtt zb~Zge@JF|#Gsk^5$-|(OPI73po|WZh<`UxaH#Y2!&p05Ph?H)d3Bc3J4sDi$f(6K`?&D&~eHVuE@_Prkt>_&8&aq=OzoN!ANkvho;qIX(g|d#EKQbJ@;-%_iARmgSF1fEK z@B4W@5mDME7AzfL**c&2#B7xO9>rA4x$rM{N=%0=goumK1kL{TF@CSk0yvqR2oo&m z)?nyiL$9~Jt(qnEuWt9Hc_duim%|zJQYiaF*~orVNDvJB;`%ZW_2x%Uu01LeX-JP& zD&fas6d3=igAgcfeki79{5!XPHHYR#nfLYRKv^wkv~cnEbLHMwQ8%yCZI^rK!D2qT zk40Vg;e!_!3d56&umIuidN?6MTZFzHot}AdqKzDh#w0s`)cV!2A74RSH1@lDXtC38 z+UhO4A9?oZEOV{bIgGd1{2qMR&xT+}q!=I8m)W23v!W2WPC?Tf!F!e%_(m^lQZtq* zYwi}gY(KZ*Y^OWRNj$Ph#uEEBM+wtN8QFQ@^`GDOln^ioNrmtvzNNi*qS5lPHxI96#sMil*teLVaa%$msF>@5p#SjT%q8|<4ZOUB#!-kG+|eFSED z!|3c8fXaym9qH`L;pmqTWcG}WE$(h1sZ3seM>)E3ptoP<;~h~qe6XA)lGVanf&->P zjZwi;_;Dt+bYdAeD_XSQ-DgXRXqLv`3Wcgl}myA-JlzBBIh zWq4Q*9#(zjAk_H8VS_AJ`?OS*^gB-rp|~qt;v(C5ef=SErv;~zL64hW`#g!UZQcvZ zF6Ra@S@YhVSkSWVAY=Z1w)w-hfJDRwKTUH0o-OG5TlW0HDH36hIjnP=?A+8u1)Qyy5U8Gi$! zt^!vy|f=YHfQ`ZRK?D zXXn*kItRg50vr2+_hV5kjOleg#s~z(J2p#`=1Tq4#JS`MC^e4p&s7Ir=3m(K$LW#` z=ULCoWtna!so+QQ*JHb~6Ps9_&Ag>9qsUskp0pKbi`n?(u3&@QT!?}N}rXn z>1eHi6(@LicU*AR1obe+nbzTCD#VTJ`PFLRT(nc$NWrhsgRwFni*D(#?W^x=J6?|b zENSc^D}s>Y55)PzFs2d_2;yh89E0ZIgs&>6JV=pL6k9g_(`$04EoY+Zjn}}8e#n83 zJ=zB>BU<253Erdo$wE4^+@QQJFZyAj#(InFlN;!UGg96R@{Y&%OlGG;dM)^X8=Ddw@&2Vx?zui$tO z-{zgaU7&F!xs=e`Mn}r+xrdIAmkraRN_7P1?qu1|TZ%1QR(Mn?k+pq`Xys2v9Gs=a z?r@g&;UKcM#?36r9k*eVD(}9qe8?irotsn0+eHH8*4 zPX@Lusr)$J%8jarx5ssEJ?twFyu4kAbrf`96_z{6at^&UkyDzFa69RXP>PeK+dAWqE5<5P+aHa zs<<*+OO_2ObTXau%y)Nn{(p5`XIPWlvi|asjYcui;E@)Ig{YKBXi}spqC!-P5owwL z3L*+9;0C0G!xoN;4KNfDaElv>1#DMDglI&MAVoK2+c2Pr8&sl*1dYj=^>NRS`{O&%YV25@5*eoOvpD_(xdKsnqb^`T}bm;n0BN9ben1Ynyi*OOf;qLpf^ z!T{}GzkXSszN_Xqzp>}S*Im)_Y8~2|B*ybw(U=Q)5_NcMkT;)1&52YQJB)Tn%kPK! z@3;^AI){B(&UOv<{v9KKJrInkdcXV0%O1%1=7vYV*j?v(Kp~arZio$#(A@$kYB3aM zRdm4!^Je15%66($EkCIWGhi@=kNAyLJ3ydlJnCpPuxH0+OA}J)+t8d7nT->##Nz4w-L=S7ExQt=Rx}S*mpT91(>t~qe7tM%e|O)TIO^dP zfo61GNS=cJbLutqUh84?7X#bq)bv57s&D_zm{+xNv7vHjb=_}j-Lrj-Ss*pcD@ts$ z)5Dol8Z_&*1@JdAQE7SL$*!TXI|YE7q=YGkIiUeLvT0)14Q-ivs|+cqeT6DTi9eQ)h?Pu9pqmH51B* zFMd|;l2@D4*56|EhMFlDxl2i<8qq=c+AhMYS3(A28#3DZ;_Ln>RA3q#IAdJq7M#N> zTZ8t=_>lq0=W&w|bdQ^sy&m^@KR)mNi3|1<6|OL(0KLtP#I6ix$2b{-Y9GP5I7 z8AJUSCnlia5vWawX%ZLWTC2UV$cn^sfv68W!6)QO;ZjnX=7#`$ZPRG~irfl)ZUJ^D z{lUk?(*SU7XIiS^H{Lpxn%542#PgxdeG)Ociej#(uvX)z;Z3)<16Yhd z-sv?qQ5D4a)ZYoYPRep2Zvom@U)HKq*54ZEwdaEq^FZG#(CyG!=Vw(0j8CCmP~`_z z=OR^i&WkDCf2cLvWm@d?)mEgme{hA(o#xAL023LZ3(82SGRg6jJF7$kZ4! z6*FTm4y6v~CP!3$+fxg{QeFo24<3iucgI!oyjV|9Dsx}r~4X@lt^VaH$u zD?87}1Jh=?G8OYg*ts2k;X9{f*Za?yu8IUUfyuQ**wbcWT+KncjD^qQ3h&w2+S(Mj zZM~?Ot%ggTIHwkBkL-4&jI5R=B+MCOR42bKzC2M>l?1%x2Iv7amIfQ1B#wwfD`z|m z+E?G+o(tde*Ws?;Wo4p#Yy>Nnf|*b<nj@-s(rZ)-U@ z(Xe(qZ1(_dH|J3yWu|bAPINK}DwF(kZ>FKx(?ZmU^KFC6*bh$;FKGh~pH1 zozA+kgcIk9@2aAwEJ=VYizT!sxDXX$N?XDiGKaaT-OU@Ib=~4DmgEk&{2D@IvyjF* zuF@sDcuuqx_FAgx;B@@8gqjMh!kQeEKA*y4+q+^4&uc0|>M;$Xb+ z@X%eUx1m%$WSP}Qchx68NQ?dO!h`6;Quq+A1(RORsQ-;6bZ90vj#^0(7>cLR+-_;9 zCd@b~B5V>$tpjkQU#BD%9^zu7-l>U8nzt+XuX5cYDCHYaX5t~~3?lpa;)Mr>q;5XW zu(Th;fr}-GkP`K)u97(#UB|L3f;H7Cd#Pox+auV`=m?a=mSv1v)(V!E=$%gkIJZ;` zZj{Lb@bhs%bRa znZw9cD$cDFVHPtpXwY1K)wys@LS~;!qdqkR>@&RtP>?M^>xe{4N#EtZy4zZ5Ar$ZF zV=X=(!xin-58MC<+b~;jk8Q|3B3THGIA$cM8Bg)Yd6ygP#i?4VrX3OvP_k5i{Cppw z-{$XwrJ-+X$ccJ(Q{|?T@U9=-?qlsfA43%8t247KZn?`+C4e`b-e^(df*iW66=Oc2 z3w9UhohfdY@pH1MZ}vc<1osV(2CGG)Ree$E-T;8>$zw*>x-505b&4(shMGIjbAfLS zEZ3ys(`SmCWc(75)^=aKer}>67qj^nGKtCK{35I|tA}wQa!uM!suX%Gb~ylORGGc( ze^|m|N!}G0#Ph|;wSXz`SByQM>lPM#8>mdSQs`7RxkXaSAADYA24u6xWqkIXY?o%z z%TEFL+wNW^&nrvaA1_#P%&Hbzrjl!*hIft>F0@g0IVydUU4MJgS3_3Js8{*>|G2jC z4%n#cOy9b2Xf&Pw=14;0Dtf00C^Z$I-v05OqtvN9>sAC&oV1Tk;;ku7VR`sQK4oFq zQ8)yoZNuTwV$t13|GCUIC{ID_r7M5&R*zhsxbrkg;EgMtL|9ne=^}BM!dxV!KDeXkWA^MfQTkQEt8~t>JznNh%ULvn@dbQ2cyf} z|C%ns#NJU}SHU(7Pg$<&8uDK>d5GZJ&`;CcfGP(~b-#UusXevc^q!km1X6_wVMqGk z^m&ZS6#42?p4c_t1TA$_+}h1L2c<<=$k%;v+D!<@j5hs|{>d18>~~v#oq4yGyS@QP zgTX2oJbEy@eJbo-f{ZQ>-nmB-#AqWcHbMQXFi*T)0n!(HIexz=pp<(O*DMh7CMupX z)ei1ZYuIW~E={-ND*nD;okiZdm!?^|LjLZhs*FHZvWld5TDj zcvWB)`-1Me9bu`*4M=CO6ye=pMgxlgYvsh2rV#5Z$hFKw0GX30%oufb=hJ0BFIJH` z+Fii4gQ+7!)8K^yc*PVEW^#f!|BW0Q5*`IewQ5YDFh?{x1L7tlaUAX@3Y+D>6FPVf zJzOGex~H34`8eq+TL$FsHm+27RS>3$CG;>0Jj4*1ukX$za})*b^S5p}I2jbFCHLsA zzYwAyftMz`uo2c8ieQcy-p&9iP3fMk(uRw+OlBPm`KCLei6g!|Vnk*-kjs>A25MTE z5GLDMV$70AC0j-tx*0sCruvKh{fSM)3X}13U>m|KeaOb`9^}v^44!$`06-JHf@L4EKyxV)M!8cL zi5p9kF97RiAT92!e?%9CP=qX3wyv^A8q!w%07d(9f-U))uDgsr4FDVL;|%r)fw}-@ zlB$F79X^EKYF%8J7mU?3VzJoYQ0<;NczW1jH4=4kEh_)q|^9wj zIsn-SsmRx0_EJ7(6WypwptIwZ)-T<__UgUu?BXt zoIf|a!5`?&JEb$w2PZSqhA>J;GIA^rJ-Cpz8MKX~bcqZNOUzPtu|NMvEP>+cO;V*W zNQ8YPENkr!)lN+tlxB79RUD20$)+_P6Jc`+4q@%Kno{F+#1qR*zrj%T>nTSceO?a5 zyqGDa59#G6k*RXu6+#=e=e!~i1Y&15!cHmE6sLh_K%Ppv$tFE-Le3RQs-nx5LB>gy z5A))kwkxWSy73{@I{%{DY8X+2o{CLJb~R$3r=oT^P~Xo$2lKz8?Z!3QLn$5l#L2k2 zb1=?UT&c<8!&9gW1M&jI!5%dhJbD3nQXpaeNJ>=zR+EL!4iY(nMBQI+|2J+Hw-WMr z08Mt9h8(PGbY?zKtk=cqw(yW}1A#htn* z8&}5Y>$uc>Lv!bSuWQ5UB&ct7*jiZAFpxz|%xO&5kg zzlf?6xy7H3G^*wvP5scW*Wf(<&eP!YIUf%&HT?K)RWmKg$G^=mSoi~;&9dU%{o}WV z#BX;9+q)fpVU`>Vdo~AtYK)`7z*H;dc-e|q6Qt;3J0APUL!~g&Q diff --git a/dashboard/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/dashboard/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png deleted file mode 100644 index ed4cc16421680a50164ba74381b4b35ceaa0ccfc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3276 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 N;OXk;vd$@?2>?>Ex^Vyi diff --git a/dashboard/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/dashboard/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png deleted file mode 100644 index bcbf36df2f2aaaa0a63c7dabc94e600184229d0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5933 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 f$KZHO*s&vt|9^w-6P?|#0pRK8NSwWJ?9znhg z3{`3j3=J&|48MRv4KElNN(~qoUL`OvSj}Ky5HFasE6@fg!ItFh?!xdN1Q+aGJ{c&& zS>O>_%)r1c48n{Iv*t(u1=&kHeO=ifbFy+6aSK)V_AxLppYn8Z42d|rc6w}vOsL55 z`t&mC&y2@JTEyg!eDiFX^k#CC!jq%>erB=yHqUP0XcDOTw6ko}L zX;EmMrq(fKk*eygEuA616;0)>@A{TK|55PV@70 z$OfzS*(VJxQev3J?yY?O=ul(v`fp}?u9z`JK3ugibK>)DyCwImZOF4d{xK%%Ks1*} zv$oa)9anR%lXIBUqYnhLmT>VOzHfNP?ZwJNZ!5$s9M08RynIvaXw>@G^T9@r9^KH1 zVy??F&uuk)bH9Y4pQY!hP58i_H6 znl-NcuCpLV6ZWU;4C zu@9exF&OZi`Bovq_m%T+WhU2kvkz@^_LpycBvqm3bMpLw8X-Or5sL>0AKE1$(k_L=_Zc=CUq#=x1-QZf)G7nHu@fmsQ1eN_N3+nTEz`4HI4Z6uVlE zJH+X&det8JU?tO?upcM4Z=cV!JV;yF>FfL5Q$M|W_2Z!P`S=}Wzp|_1^#d%e?_H`> zV@%vA$+bFVqhw9`U;TfP|5|PD{||OiYdor8P*i??|NJcb%kzT_73*7WE?Ua5hAnR2 z=7WE=PhTlJ#ZeRznjTUb;`E(wkMZrj4e|Hilz-mK>9cZHQY**5TUPw~u}k;u73KI}xAx!0m-)GVia|x^d3p~s_9gh83jA&Ra<8rM%`>U3x69t&NzbwWY}7Ar?)FK#IZ0z|d0H0EkRO w3{9;}4Xg|ebq&m|3=9_N6z8I7$jwj5OsmAL;bP(Gi$Dzwp00i_>zopr02+f8CIA2c diff --git a/dashboard/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/dashboard/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png deleted file mode 100644 index e71a726136a47ed24125c7efc79d68a4a01961b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14800 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 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/dashboard/macos/Runner/Configs/AppInfo.xcconfig b/dashboard/macos/Runner/Configs/AppInfo.xcconfig deleted file mode 100644 index 1cb904065..000000000 --- a/dashboard/macos/Runner/Configs/AppInfo.xcconfig +++ /dev/null @@ -1,14 +0,0 @@ -// Application-level settings for the Runner target. -// -// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the -// future. If not, the values below would default to using the project name when this becomes a -// 'flutter create' template. - -// The application's name. By default this is also the title of the Flutter window. -PRODUCT_NAME = app_flutter - -// The application's bundle identifier -PRODUCT_BUNDLE_IDENTIFIER = com.example.appFlutter - -// The copyright displayed in application information -PRODUCT_COPYRIGHT = Copyright © 2021 com.example. All rights reserved. diff --git a/dashboard/macos/Runner/Configs/Debug.xcconfig b/dashboard/macos/Runner/Configs/Debug.xcconfig deleted file mode 100644 index 36b0fd946..000000000 --- a/dashboard/macos/Runner/Configs/Debug.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "../../Flutter/Flutter-Debug.xcconfig" -#include "Warnings.xcconfig" diff --git a/dashboard/macos/Runner/Configs/Release.xcconfig b/dashboard/macos/Runner/Configs/Release.xcconfig deleted file mode 100644 index dff4f4956..000000000 --- a/dashboard/macos/Runner/Configs/Release.xcconfig +++ /dev/null @@ -1,2 +0,0 @@ -#include "../../Flutter/Flutter-Release.xcconfig" -#include "Warnings.xcconfig" diff --git a/dashboard/macos/Runner/Configs/Warnings.xcconfig b/dashboard/macos/Runner/Configs/Warnings.xcconfig deleted file mode 100644 index 42bcbf478..000000000 --- a/dashboard/macos/Runner/Configs/Warnings.xcconfig +++ /dev/null @@ -1,13 +0,0 @@ -WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings -GCC_WARN_UNDECLARED_SELECTOR = YES -CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES -CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE -CLANG_WARN__DUPLICATE_METHOD_MATCH = YES -CLANG_WARN_PRAGMA_PACK = YES -CLANG_WARN_STRICT_PROTOTYPES = YES -CLANG_WARN_COMMA = YES -GCC_WARN_STRICT_SELECTOR_MATCH = YES -CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES -CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES -GCC_WARN_SHADOW = YES -CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/dashboard/macos/Runner/DebugProfile.entitlements b/dashboard/macos/Runner/DebugProfile.entitlements deleted file mode 100644 index dddb8a30c..000000000 --- a/dashboard/macos/Runner/DebugProfile.entitlements +++ /dev/null @@ -1,12 +0,0 @@ - - - - - com.apple.security.app-sandbox - - com.apple.security.cs.allow-jit - - com.apple.security.network.server - - - diff --git a/dashboard/macos/Runner/Info.plist b/dashboard/macos/Runner/Info.plist deleted file mode 100644 index 4789daa6a..000000000 --- a/dashboard/macos/Runner/Info.plist +++ /dev/null @@ -1,32 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIconFile - - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - APPL - CFBundleShortVersionString - $(FLUTTER_BUILD_NAME) - CFBundleVersion - $(FLUTTER_BUILD_NUMBER) - LSMinimumSystemVersion - $(MACOSX_DEPLOYMENT_TARGET) - NSHumanReadableCopyright - $(PRODUCT_COPYRIGHT) - NSMainNibFile - MainMenu - NSPrincipalClass - NSApplication - - diff --git a/dashboard/macos/Runner/MainFlutterWindow.swift b/dashboard/macos/Runner/MainFlutterWindow.swift deleted file mode 100644 index 2722837ec..000000000 --- a/dashboard/macos/Runner/MainFlutterWindow.swift +++ /dev/null @@ -1,15 +0,0 @@ -import Cocoa -import FlutterMacOS - -class MainFlutterWindow: NSWindow { - override func awakeFromNib() { - let flutterViewController = FlutterViewController.init() - let windowFrame = self.frame - self.contentViewController = flutterViewController - self.setFrame(windowFrame, display: true) - - RegisterGeneratedPlugins(registry: flutterViewController) - - super.awakeFromNib() - } -} diff --git a/dashboard/macos/Runner/Release.entitlements b/dashboard/macos/Runner/Release.entitlements deleted file mode 100644 index 852fa1a47..000000000 --- a/dashboard/macos/Runner/Release.entitlements +++ /dev/null @@ -1,8 +0,0 @@ - - - - - com.apple.security.app-sandbox - - - diff --git a/dashboard/pubspec.yaml b/dashboard/pubspec.yaml deleted file mode 100644 index b31e30944..000000000 --- a/dashboard/pubspec.yaml +++ /dev/null @@ -1,87 +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. - -name: flutter_dashboard -description: The Flutter dashboard. -publish_to: none - -# The following defines the version and build number for your application. -# A version number is three numbers separated by dots, like 1.2.43 -# followed by an optional build number separated by a +. -# Both the version and the builder number may be overridden in flutter -# build by specifying --build-name and --build-number, respectively. -# In Android, build-name is used as versionName while build-number used as versionCode. -# Read more about Android versioning at https://developer.android.com/studio/publish/versioning -# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. -# Read more about iOS versioning at -# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.0.0+1 - -environment: - sdk: '>=2.18.0 <4.0.0' - -dependencies: - collection: 1.18.0 - fixnum: 1.1.0 - flutter: - sdk: flutter - google_sign_in: 6.1.0 - google_sign_in_platform_interface: any # Match google_sign_in - google_sign_in_web: any # Match google_sign_in - http: 0.13.5 - protobuf: 2.1.0 - provider: 6.0.5 - url_launcher: 6.1.11 - flutter_app_icons: 0.0.8 - truncate: 3.0.1 - url_launcher_web: 2.0.16 - -dev_dependencies: - build_runner: 2.4.4 - flutter_lints: 2.0.1 - flutter_test: - sdk: flutter - mockito: 5.4.1 # Remember to run ./regen_mocks.sh! - path: 1.8.3 - url_launcher_platform_interface: 2.1.2 - -# For information on the generic Dart part of this file, see the -# following page: https://dart.dev/tools/pub/pubspec - -# The following section is specific to Flutter. -flutter: - - # The following line ensures that the Material Icons font is - # included with your application, so that you can use the icons in - # the material Icons class. - uses-material-design: true - - assets: - - assets/ - - # An image asset can refer to one or more resolution-specific "variants", see - # https://flutter.dev/assets-and-images/#resolution-aware. - - # For details regarding adding assets from package dependencies, see - # https://flutter.dev/assets-and-images/#from-packages - - # To add custom fonts to your application, add a fonts section here, - # in this "flutter" section. Each entry in this list should have a - # "family" key with the font family name, and a "fonts" key with a - # list giving the asset and other descriptors for the font. For - # example: - # fonts: - # - family: Schyler - # fonts: - # - asset: fonts/Schyler-Regular.ttf - # - asset: fonts/Schyler-Italic.ttf - # style: italic - # - family: Trajan Pro - # fonts: - # - asset: fonts/TrajanPro.ttf - # - asset: fonts/TrajanPro_Bold.ttf - # weight: 700 - # - # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages diff --git a/dashboard/regen_mocks.sh b/dashboard/regen_mocks.sh deleted file mode 100755 index f2e54b1db..000000000 --- a/dashboard/regen_mocks.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash -# 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. - -flutter pub get - -echo "(Re)generating mocks." - -dart run build_runner build --delete-conflicting-outputs diff --git a/dashboard/test/build_dashboard_page_test.dart b/dashboard/test/build_dashboard_page_test.dart deleted file mode 100644 index c75554047..000000000 --- a/dashboard/test/build_dashboard_page_test.dart +++ /dev/null @@ -1,601 +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:flutter/material.dart'; -import 'package:flutter_app_icons/flutter_app_icons_platform_interface.dart'; -import 'package:flutter_dashboard/build_dashboard_page.dart'; -import 'package:flutter_dashboard/model/branch.pb.dart'; -import 'package:flutter_dashboard/model/build_status_response.pb.dart'; -import 'package:flutter_dashboard/model/commit_status.pb.dart'; -import 'package:flutter_dashboard/service/cocoon.dart'; -import 'package:flutter_dashboard/service/dev_cocoon.dart'; -import 'package:flutter_dashboard/service/google_authentication.dart'; -import 'package:flutter_dashboard/state/build.dart'; -import 'package:flutter_dashboard/widgets/error_brook_watcher.dart'; -import 'package:flutter_dashboard/widgets/user_sign_in.dart'; -import 'package:flutter_dashboard/widgets/state_provider.dart'; -import 'package:flutter_dashboard/widgets/task_box.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; - -import 'utils/fake_build.dart'; -import 'utils/fake_flutter_app_icons.dart'; -import 'utils/fake_google_account.dart'; -import 'utils/golden.dart'; -import 'utils/mocks.dart'; -import 'utils/output.dart'; -import 'utils/task_icons.dart'; - -void main() { - late MockGoogleSignInService fakeAuthService; - - final Type dropdownButtonType = DropdownButton( - onChanged: (_) {}, - items: const >[], - ).runtimeType; - - void configureView(TestFlutterView view) { - // device pixel ratio of 1.0 works well on web app and emulator - // If not set, flutter test uses a Pixel 4 device pixel ratio of roughly 2.75, which doesn't quite work - // I am using the default settings of Pixel 4 in this test, as referenced in the link below - // https://android.googlesource.com/platform/external/qemu/+/b5b78438ae9ff3b90aafdab0f4f25585affc22fb/android/avd/hardware-properties.ini - view.devicePixelRatio = 1.0; - view.physicalSize = const Size(1080, 2280); - addTearDown(view.reset); - } - - setUp(() { - fakeAuthService = MockGoogleSignInService(); - when(fakeAuthService.isAuthenticated).thenReturn(true); - when(fakeAuthService.user).thenReturn(FakeGoogleSignInAccount()); - - FlutterAppIconsPlatform.instance = FakeFlutterAppIcons(); - }); - - testWidgets('shows sign in button', (WidgetTester tester) async { - configureView(tester.view); - final MockCocoonService fakeCocoonService = MockCocoonService(); - throwOnMissingStub(fakeCocoonService); - when(fakeCocoonService.fetchFlutterBranches()).thenAnswer((_) => Completer>>().future); - when(fakeCocoonService.fetchRepos()).thenAnswer((_) => Completer>>().future); - when( - fakeCocoonService.fetchCommitStatuses( - branch: anyNamed('branch'), - repo: anyNamed('repo'), - ), - ).thenAnswer((_) => Completer>>().future); - when( - fakeCocoonService.fetchTreeBuildStatus( - branch: anyNamed('branch'), - repo: anyNamed('repo'), - ), - ).thenAnswer((_) => Completer>().future); - - final BuildState buildState = BuildState( - cocoonService: fakeCocoonService, - authService: fakeAuthService, - ); - - await tester.pumpWidget( - MaterialApp( - theme: ThemeData(useMaterial3: false), - home: ValueProvider( - value: buildState, - child: ValueProvider( - value: buildState.authService, - child: const BuildDashboardPage(), - ), - ), - ), - ); - expect(find.byType(UserSignIn), findsOneWidget); - - await tester.pumpWidget(Container()); - buildState.dispose(); - }); - - testWidgets('shows settings button', (WidgetTester tester) async { - configureView(tester.view); - final BuildState fakeBuildState = FakeBuildState()..authService = fakeAuthService; - - await tester.pumpWidget( - MaterialApp( - theme: ThemeData(useMaterial3: false), - home: ValueProvider( - value: fakeBuildState, - child: ValueProvider( - value: fakeBuildState.authService, - child: const BuildDashboardPage(), - ), - ), - ), - ); - - expect(find.byIcon(Icons.settings), findsOneWidget); - }); - - testWidgets('shows file a bug button', (WidgetTester tester) async { - configureView(tester.view); - final BuildState fakeBuildState = FakeBuildState()..authService = fakeAuthService; - - await tester.pumpWidget( - MaterialApp( - theme: ThemeData(useMaterial3: false), - home: ValueProvider( - value: fakeBuildState, - child: ValueProvider( - value: fakeBuildState.authService, - child: const BuildDashboardPage(), - ), - ), - ), - ); - - expect(find.byIcon(Icons.bug_report), findsOneWidget); - }); - - testWidgets('shows key button & legend', (WidgetTester tester) async { - configureView(tester.view); - final BuildState fakeBuildState = FakeBuildState()..authService = fakeAuthService; - - await tester.pumpWidget( - MaterialApp( - theme: ThemeData(useMaterial3: false), - home: ValueProvider( - value: fakeBuildState, - child: ValueProvider( - value: fakeBuildState.authService, - child: const BuildDashboardPage(), - ), - ), - ), - ); - - expect(find.byIcon(Icons.info_outline), findsOneWidget); - - await tester.tap(find.byIcon(Icons.info_outline)); - await tester.pump(); - - for (final String status in TaskBox.statusColor.keys) { - expect(find.text(status), findsOneWidget); - } - expect(find.text('Flaky'), findsOneWidget); - expect(find.text('Ran more than once'), findsOneWidget); - }); - - testWidgets('shows branch and repo dropdown button when screen is decently large', (WidgetTester tester) async { - configureView(tester.view); - final BuildState fakeBuildState = FakeBuildState()..authService = fakeAuthService; - - await tester.pumpWidget( - MaterialApp( - theme: ThemeData(useMaterial3: false), - home: ValueProvider( - value: fakeBuildState, - child: ValueProvider( - value: fakeBuildState.authService, - child: const BuildDashboardPage(), - ), - ), - ), - ); - - expect(find.byType(dropdownButtonType), findsNWidgets(2)); - - expect(find.text('repo: '), findsOneWidget); - expect((tester.widget(find.byKey(const Key('repo dropdown'))) as DropdownButton).value, equals('flutter')); - - expect(find.text('branch: '), findsOneWidget); - expect((tester.widget(find.byKey(const Key('branch dropdown'))) as DropdownButton).value, equals('master')); - }); - - testWidgets('shows enabled Refresh GitHub Commits button when isAuthenticated', (WidgetTester tester) async { - configureView(tester.view); - final BuildState fakeBuildState = FakeBuildState()..authService = fakeAuthService; - - await tester.pumpWidget( - MaterialApp( - theme: ThemeData(useMaterial3: false), - home: ValueProvider( - value: fakeBuildState, - child: ValueProvider( - value: fakeBuildState.authService, - child: const BuildDashboardPage(), - ), - ), - ), - ); - - // Open settings overlay - await tester.tap(find.byIcon(Icons.settings)); - await tester.pump(); - await tester.pump(const Duration(seconds: 1)); // Finish the menu animation. - - final Finder labelFinder = find.text('Refresh GitHub Commits'); - - expect(labelFinder, findsOneWidget); - - final TextButton button = tester.element(labelFinder).findAncestorWidgetOfExactType()!; - - expect(button.onPressed, isNotNull, reason: 'The button should have a non-null onPressed attribute.'); - }); - - testWidgets('shows disabled Refresh GitHub Commits button when !isAuthenticated', (WidgetTester tester) async { - configureView(tester.view); - final BuildState fakeBuildState = FakeBuildState()..authService = fakeAuthService; - when(fakeAuthService.isAuthenticated).thenReturn(false); - - await tester.pumpWidget( - MaterialApp( - theme: ThemeData(useMaterial3: false), - home: ValueProvider( - value: fakeBuildState, - child: ValueProvider( - value: fakeBuildState.authService, - child: const BuildDashboardPage(), - ), - ), - ), - ); - - // Open settings overlay - await tester.tap(find.byIcon(Icons.settings)); - await tester.pump(); - await tester.pump(const Duration(seconds: 1)); // Finish the menu animation. - - final Finder labelFinder = find.text('Refresh GitHub Commits'); - final TextButton button = tester.element(labelFinder).findAncestorWidgetOfExactType()!; - - expect(button.onPressed, isNull, reason: 'The button should have a null onPressed attribute.'); - }); - - testWidgets('shows loading when fetch tree status is null', (WidgetTester tester) async { - configureView(tester.view); - final BuildState fakeBuildState = FakeBuildState() - ..isTreeBuilding = null - ..authService = fakeAuthService; - - await tester.pumpWidget( - MaterialApp( - theme: ThemeData(useMaterial3: false), - home: ValueProvider( - value: fakeBuildState, - child: ValueProvider( - value: fakeBuildState.authService, - child: const BuildDashboardPage(), - ), - ), - ), - ); - - expect(find.text('Loading...'), findsOneWidget); - - final AppBar appbarWidget = find.byType(AppBar).evaluate().first.widget as AppBar; - expect(appbarWidget.backgroundColor, Colors.grey[850]); - expect(tester, meetsGuideline(textContrastGuideline)); - }); - - testWidgets('shows loading when fetch tree status is null, dark mode', (WidgetTester tester) async { - configureView(tester.view); - final BuildState fakeBuildState = FakeBuildState() - ..isTreeBuilding = null - ..authService = fakeAuthService; - - await tester.pumpWidget( - MaterialApp( - theme: ThemeData.dark(useMaterial3: false), - home: ValueProvider( - value: fakeBuildState, - child: ValueProvider( - value: fakeBuildState.authService, - child: const BuildDashboardPage(), - ), - ), - ), - ); - - expect(find.text('Loading...'), findsOneWidget); - - final AppBar appbarWidget = find.byType(AppBar).evaluate().first.widget as AppBar; - expect(appbarWidget.backgroundColor, Colors.grey[850]); - expect(tester, meetsGuideline(textContrastGuideline)); - }); - - testWidgets('shows tree closed when fetch tree status is false', (WidgetTester tester) async { - configureView(tester.view); - final BuildState fakeBuildState = FakeBuildState() - ..isTreeBuilding = false - ..authService = fakeAuthService; - - await tester.pumpWidget( - MaterialApp( - theme: ThemeData(useMaterial3: false), - home: ValueProvider( - value: fakeBuildState, - child: ValueProvider( - value: fakeBuildState.authService, - child: const BuildDashboardPage(), - ), - ), - ), - ); - - // Verify the "Tree is Closed" message is wrapped in a [Tooltip]. - final Finder tooltipFinder = find.byWidgetPredicate((Widget widget) { - return widget is Tooltip && (widget.message?.contains('Tree is Closed') ?? false); - }); - expect(tooltipFinder, findsOneWidget); - - final AppBar appbarWidget = find.byType(AppBar).evaluate().first.widget as AppBar; - expect(appbarWidget.backgroundColor, Colors.red); - expect(tester, meetsGuideline(textContrastGuideline)); - }); - - testWidgets('shows tree closed when fetch tree status is false, dark mode', (WidgetTester tester) async { - configureView(tester.view); - final BuildState fakeBuildState = FakeBuildState() - ..isTreeBuilding = false - ..authService = fakeAuthService; - - await tester.pumpWidget( - MaterialApp( - theme: ThemeData.dark(useMaterial3: false), - home: ValueProvider( - value: fakeBuildState, - child: ValueProvider( - value: fakeBuildState.authService, - child: const BuildDashboardPage(), - ), - ), - ), - ); - - // Verify the "Tree is Closed" message is wrapped in a [Tooltip]. - final Finder tooltipFinder = find.byWidgetPredicate((Widget widget) { - return widget is Tooltip && (widget.message?.contains('Tree is Closed') ?? false); - }); - expect(tooltipFinder, findsOneWidget); - - final AppBar appbarWidget = find.byType(AppBar).evaluate().first.widget as AppBar; - expect(appbarWidget.backgroundColor, Colors.red[800]); - expect(tester, meetsGuideline(textContrastGuideline)); - }); - - testWidgets('shows tree open when fetch tree status is true', (WidgetTester tester) async { - configureView(tester.view); - final BuildState fakeBuildState = FakeBuildState() - ..isTreeBuilding = true - ..authService = fakeAuthService; - - await tester.pumpWidget( - MaterialApp( - theme: ThemeData(useMaterial3: false), - home: ValueProvider( - value: fakeBuildState, - child: ValueProvider( - value: fakeBuildState.authService, - child: const BuildDashboardPage(), - ), - ), - ), - ); - - expect(find.text('Tree is Open'), findsOneWidget); - - final AppBar appbarWidget = find.byType(AppBar).evaluate().first.widget as AppBar; - expect(appbarWidget.backgroundColor, Colors.green); - expect(tester, meetsGuideline(textContrastGuideline)); - }); - - testWidgets('shows tree open when fetch tree status is true, dark mode', (WidgetTester tester) async { - configureView(tester.view); - final BuildState fakeBuildState = FakeBuildState() - ..isTreeBuilding = true - ..authService = fakeAuthService; - - await tester.pumpWidget( - MaterialApp( - theme: ThemeData.dark(useMaterial3: false), - home: ValueProvider( - value: fakeBuildState, - child: ValueProvider( - value: fakeBuildState.authService, - child: const BuildDashboardPage(), - ), - ), - ), - ); - - expect(find.text('Tree is Open'), findsOneWidget); - - final AppBar appbarWidget = find.byType(AppBar).evaluate().first.widget as AppBar; - expect(appbarWidget.backgroundColor, Colors.green[800]); - expect(tester, meetsGuideline(textContrastGuideline)); - }); - - testWidgets('show error snackbar when error occurs', (WidgetTester tester) async { - configureView(tester.view); - String? lastError; - final FakeBuildState buildState = FakeBuildState() - ..errors.addListener((String message) => lastError = message) - ..authService = fakeAuthService; - - await tester.pumpWidget( - MaterialApp( - theme: ThemeData(useMaterial3: false), - home: ValueProvider( - value: buildState, - child: ValueProvider( - value: buildState.authService, - child: const BuildDashboardPage(), - ), - ), - ), - ); - - expect(lastError, isNull); - - // propagate the error message - await checkOutput( - block: () async { - buildState.errors.send('ERROR'); - }, - output: [ - 'ERROR', - ], - ); - await tester.pump(); - - await tester.pump(const Duration(milliseconds: 750)); // open animation for snackbar - - expect(find.text(lastError!), findsOneWidget); - - // Snackbar message should go away after its duration - await tester.pump(ErrorBrookWatcher.errorSnackbarDuration); // wait the duration - await tester.pump(); // schedule animation - await tester.pump(const Duration(milliseconds: 1500)); // close animation - - expect(find.text(lastError!), findsNothing); - }); - - testWidgets('TaskGridContainer with default Settings property sheet', (WidgetTester tester) async { - configureView(tester.view); - await precacheTaskIcons(tester); - final BuildState buildState = BuildState( - cocoonService: DevelopmentCocoonService(DateTime.utc(2020)), - authService: fakeAuthService, - ); - void listener1() {} - buildState.addListener(listener1); - - await tester.pumpWidget( - MaterialApp( - theme: ThemeData(useMaterial3: false), - home: ValueProvider( - value: buildState, - child: ValueProvider( - value: buildState.authService, - child: const BuildDashboardPage(), - ), - ), - ), - ); - - await tester.tap(find.byIcon(Icons.settings)); - await tester.pump(); - - await expectGoldenMatches(find.byType(BuildDashboardPage), 'build_dashboard.defaultPropertySheet.png'); - expect(tester, meetsGuideline(textContrastGuideline)); - - await tester.pumpWidget(Container()); - buildState.dispose(); - }); - - testWidgets('TaskGridContainer with default Settings property sheet, dark mode', (WidgetTester tester) async { - configureView(tester.view); - await precacheTaskIcons(tester); - final BuildState buildState = BuildState( - cocoonService: DevelopmentCocoonService(DateTime.utc(2020)), - authService: fakeAuthService, - ); - void listener1() {} - buildState.addListener(listener1); - - await tester.pumpWidget( - MaterialApp( - theme: ThemeData.dark(useMaterial3: false), - home: ValueProvider( - value: buildState, - child: ValueProvider( - value: buildState.authService, - child: const BuildDashboardPage(), - ), - ), - ), - ); - - await tester.tap(find.byIcon(Icons.settings)); - await tester.pump(); - - await expectGoldenMatches(find.byType(BuildDashboardPage), 'build_dashboard.defaultPropertySheet.dark.png'); - expect(tester, meetsGuideline(textContrastGuideline)); - - await tester.pumpWidget(Container()); - buildState.dispose(); - }); - - testWidgets('ensure smooth transition between invalid states', (WidgetTester tester) async { - configureView(tester.view); - final BuildState fakeBuildState = FakeBuildState()..authService = fakeAuthService; - BuildDashboardPage controlledBuildDashboardPage = const BuildDashboardPage( - queryParameters: { - 'repo': 'flutter', - 'branch': 'flutter-release', - }, - ); - - await tester.pumpWidget( - MaterialApp( - theme: ThemeData(useMaterial3: false), - home: ValueProvider( - value: fakeBuildState, - child: ValueProvider( - value: fakeBuildState.authService, - child: controlledBuildDashboardPage, - ), - ), - ), - ); - - expect(find.byType(dropdownButtonType), findsNWidgets(2)); - // simulate a url request, which retriggers a rebuild of the widget - controlledBuildDashboardPage = const BuildDashboardPage( - queryParameters: { - 'repo': 'engine', - }, - ); - expect( - (tester.widget(find.byKey(const Key('branch dropdown'))) as DropdownButton).value, - equals('flutter-release'), - ); //invalid state: engine + flutter-release - await tester.pump(); //an invalid state will generate delayed network responses - - //if a delayed network request come in, from a previous invalid state: cocoon + engine - release, no exceptions should be raised - controlledBuildDashboardPage = const BuildDashboardPage( - queryParameters: { - 'repo': 'cocoon', - 'branch': 'engine-release', - }, - ); - }); - - testWidgets('shows branch and repo dropdown button in settings when screen is small', (WidgetTester tester) async { - tester.view.physicalSize = const Size(500, 500); - tester.view.devicePixelRatio = 1.0; - addTearDown(tester.view.reset); - final BuildState fakeBuildState = FakeBuildState()..authService = fakeAuthService; - - await tester.pumpWidget( - MaterialApp( - theme: ThemeData(useMaterial3: false), - home: ValueProvider( - value: fakeBuildState, - child: ValueProvider( - value: fakeBuildState.authService, - child: const BuildDashboardPage(), - ), - ), - ), - ); - - expect(find.byType(dropdownButtonType), findsNothing); - - await tester.tap(find.byIcon(Icons.settings)); - await tester.pump(); - - expect(find.byType(dropdownButtonType), findsNWidgets(2)); - }); -} diff --git a/dashboard/test/dashboard_navigation_drawer_test.dart b/dashboard/test/dashboard_navigation_drawer_test.dart deleted file mode 100644 index 782c7f003..000000000 --- a/dashboard/test/dashboard_navigation_drawer_test.dart +++ /dev/null @@ -1,133 +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:flutter/material.dart'; -import 'package:flutter_dashboard/main.dart'; -import 'package:flutter_dashboard/dashboard_navigation_drawer.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; -import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; - -import 'utils/fake_url_launcher.dart'; -import 'utils/wrapper.dart'; - -void main() { - void configureView(TestFlutterView view) { - view.devicePixelRatio = 1.0; - view.physicalSize = const Size(1080, 2280); - addTearDown(view.reset); - } - - group('DashboardNavigationDrawer', () { - late FakeUrlLauncher urlLauncher; - - setUp(() { - urlLauncher = FakeUrlLauncher(); - UrlLauncherPlatform.instance = urlLauncher; - }); - - testWidgets('lists all pages', (WidgetTester tester) async { - configureView(tester.view); - await tester.pumpWidget( - const MaterialApp( - title: 'Test', - home: DashboardNavigationDrawer(), - ), - ); - - expect(find.text('Build'), findsOneWidget); - expect(find.text('Framework Benchmarks'), findsOneWidget); - expect(find.text('Engine Benchmarks'), findsOneWidget); - expect(find.text('Source Code'), findsOneWidget); - await tester.drag(find.text('Source Code'), const Offset(0.0, -100)); - await tester.pump(); - expect(find.text('About Test'), findsOneWidget); - }); - - testWidgets('build navigates to build Flutter route', (WidgetTester tester) async { - configureView(tester.view); - final MockNavigatorObserver navigatorObserver = MockNavigatorObserver(); - await tester.pumpWidget( - MaterialApp( - home: const DashboardNavigationDrawer(), - initialRoute: '/', - routes: { - '/build': (BuildContext context) => const Text('i am build'), - }, - navigatorObservers: [navigatorObserver], - ), - ); - - verifyNever(navigatorObserver.didReplace()); - expect(find.text('i am build'), findsNothing); - - // Click the nav item for build - await tester.tap(find.text('Build')); - await tester.pumpAndSettle(); - - verify(navigatorObserver.didReplace(newRoute: anyNamed('newRoute'), oldRoute: anyNamed('oldRoute'))).called(1); - expect(find.text('i am build'), findsOneWidget); - }); - - testWidgets('skia perf links opens skia perf url', (WidgetTester tester) async { - configureView(tester.view); - await tester.pumpWidget( - const MaterialApp( - home: DashboardNavigationDrawer(), - ), - ); - - const String skiaPerfText = 'Engine Benchmarks'; - expect(find.text(skiaPerfText), findsOneWidget); - await tester.tap(find.text(skiaPerfText)); - await tester.pump(); - - expect(urlLauncher.launches, isNotEmpty); - expect(urlLauncher.launches.single, 'https://flutter-engine-perf.skia.org/'); - }); - - testWidgets('source code opens github cocoon url', (WidgetTester tester) async { - configureView(tester.view); - await tester.pumpWidget( - const MaterialApp( - home: DashboardNavigationDrawer(), - ), - ); - - expect(find.text('Source Code'), findsOneWidget); - await tester.tap(find.text('Source Code')); - await tester.pump(); - - expect(urlLauncher.launches, isNotEmpty); - expect(urlLauncher.launches.single, 'https://github.com/flutter/cocoon'); - }); - - testWidgets('current route shows highlighted', (WidgetTester tester) async { - configureView(tester.view); - await tester.pumpWidget(const FakeInserter(child: MyApp())); - - void test({required bool isHome}) { - final ListTile build = tester.widget(find.ancestor(of: find.text('Build'), matching: find.byType(ListTile))); - expect(build.selected, !isHome); - } - - await tester.tap(find.byIcon(Icons.menu)); - await tester.pump(); // start animation of drawer opening - await tester.pump(const Duration(seconds: 1)); // end animation of drawer opening - test(isHome: false); - - await tester.tap(find.text('Build')); - await tester.pump(); // drawer closes and new page arrives - await tester.pump(const Duration(seconds: 1)); // end of those animations - - await tester.tap(find.byIcon(Icons.menu)); - await tester.pump(); // start animation of drawer opening - await tester.pump(const Duration(seconds: 1)); // end animation of drawer opening - test(isHome: false); - }); - }); -} - -/// Class for testing interactions on [NavigatorObserver]. -class MockNavigatorObserver extends Mock implements NavigatorObserver {} diff --git a/dashboard/test/goldens/build_dashboard.defaultPropertySheet.dark.png b/dashboard/test/goldens/build_dashboard.defaultPropertySheet.dark.png deleted file mode 100644 index 899ee7b36c6429bb127e28937fe0f35ee66320d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41995 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 diff --git a/dashboard/test/logic/qualified_task_test.dart b/dashboard/test/logic/qualified_task_test.dart deleted file mode 100644 index 2e7d19ac1..000000000 --- a/dashboard/test/logic/qualified_task_test.dart +++ /dev/null @@ -1,44 +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:flutter_dashboard/logic/qualified_task.dart'; -import 'package:flutter_dashboard/model/task.pb.dart'; - -import 'package:flutter_test/flutter_test.dart'; - -void main() { - test('QualifiedTask.sourceConfigurationUrl for luci', () { - final Task luciTask = Task() - ..stageName = 'chromebot' - ..name = 'abc' - ..builderName = 'def'; - - expect( - QualifiedTask.fromTask(luciTask).sourceConfigurationUrl, - 'https://ci.chromium.org/p/flutter/builders/luci.flutter.prod/def', - ); - }); - - test('QualifiedTask.sourceConfigurationUrl for google test', () { - final Task googleTestTask = Task()..stageName = 'google_internal'; - - expect(QualifiedTask.fromTask(googleTestTask).sourceConfigurationUrl, 'https://flutter-rob.corp.google.com'); - }); - - test('QualifiedTask.sourceConfigurationUrl for dart-internal', () { - final Task dartInternalTask = Task()..stageName = 'dart-internal'; - - expect( - QualifiedTask.fromTask(dartInternalTask).sourceConfigurationUrl, - 'https://ci.chromium.org/p/dart-internal/builders/luci.flutter.prod/', - ); - }); - - test('QualifiedTask.isLuci', () { - expect(const QualifiedTask(stage: 'luci', task: 'abc').isLuci, true); - expect(const QualifiedTask(stage: 'chromebot', task: 'abc').isLuci, true); - expect(const QualifiedTask(stage: 'cocoon', task: 'abc').isLuci, true); - expect(const QualifiedTask(stage: 'google_internal', task: 'abc').isLuci, false); - }); -} diff --git a/dashboard/test/logic/task_grid_filter_test.dart b/dashboard/test/logic/task_grid_filter_test.dart deleted file mode 100644 index 36e52d549..000000000 --- a/dashboard/test/logic/task_grid_filter_test.dart +++ /dev/null @@ -1,324 +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:flutter_dashboard/logic/qualified_task.dart'; -import 'package:flutter_dashboard/logic/task_grid_filter.dart'; -import 'package:flutter_dashboard/model/commit.pb.dart'; -import 'package:flutter_dashboard/model/commit_status.pb.dart'; -import 'package:flutter_dashboard/model/task.pb.dart'; - -import 'package:flutter_test/flutter_test.dart'; - -void main() { - void testDefault(TaskGridFilter filter) { - expect(filter.toMap(), isEmpty); - expect(filter.taskFilter, null); - expect(filter.authorFilter, null); - expect(filter.messageFilter, null); - expect(filter.hashFilter, null); - expect(filter.showiOS, true); - expect(filter.showStaging, false); - - expect(filter.matchesTask(QualifiedTask.fromTask(Task())), true); - expect(filter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'foo')), true); - expect(filter.matchesTask(QualifiedTask.fromTask(Task()..stageName = 'foo')), true); - expect(filter.matchesTask(QualifiedTask.fromTask(Task()..stageName = StageName.luci)), true); - - expect(filter.matchesCommit(CommitStatus()), true); - expect(filter.matchesCommit(CommitStatus()..commit = Commit()), true); - expect(filter.matchesCommit(CommitStatus()..commit = (Commit()..author = 'joe')), true); - expect(filter.matchesCommit(CommitStatus()..commit = (Commit()..sha = '0x45c3fd')), true); - expect(filter.matchesCommit(CommitStatus()..commit = (Commit()..message = 'LGTM!')), true); - } - - test('default task grid filter', () { - testDefault(TaskGridFilter()); - }); - - test('default task grid filter from null map', () { - testDefault(TaskGridFilter.fromMap(null)); - }); - - test('default task grid filter from empty map', () { - testDefault(TaskGridFilter.fromMap({})); - }); - - test('map constructor allows unused values', () { - expect(TaskGridFilter.fromMap({'repo': 'flutter'}), TaskGridFilter()); - }); - - test('map constructor result matches field setters', () { - expect(TaskGridFilter.fromMap({}), TaskGridFilter()); - expect(TaskGridFilter.fromMap({'taskFilter': 'foo'}), TaskGridFilter()..taskFilter = RegExp('foo')); - expect( - TaskGridFilter.fromMap({'authorFilter': 'foo'}), - TaskGridFilter()..authorFilter = RegExp('foo'), - ); - expect( - TaskGridFilter.fromMap({'messageFilter': 'foo'}), - TaskGridFilter()..messageFilter = RegExp('foo'), - ); - expect(TaskGridFilter.fromMap({'hashFilter': 'foo'}), TaskGridFilter()..hashFilter = RegExp('foo')); - expect(TaskGridFilter.fromMap({'showMac': 'false'}), TaskGridFilter()..showMac = false); - expect(TaskGridFilter.fromMap({'showStaging': 'false'}), TaskGridFilter()..showStaging = false); - }); - - test('cross check on inequality', () { - final TaskGridFilter defaultFilter = TaskGridFilter(); - final List nonDefaultFilters = [ - TaskGridFilter()..taskFilter = RegExp('foo'), - TaskGridFilter()..authorFilter = RegExp('foo'), - TaskGridFilter()..messageFilter = RegExp('foo'), - TaskGridFilter()..hashFilter = RegExp('foo'), - TaskGridFilter()..showLinux = false, - ]; - for (final TaskGridFilter filter in nonDefaultFilters) { - expect(filter, isNot(equals(defaultFilter))); - expect(defaultFilter, isNot(equals(filter))); - } - for (int i = 0; i < nonDefaultFilters.length; i++) { - for (int j = 0; j < nonDefaultFilters.length; j++) { - if (i == j) { - expect(nonDefaultFilters[i], nonDefaultFilters[j]); - } else { - expect(nonDefaultFilters[i], isNot(equals(nonDefaultFilters[j]))); - } - } - } - }); - - test('staging filter show all tasks', () { - final List filters = [ - TaskGridFilter()..showStaging = true, - ]; - for (final TaskGridFilter filter in filters) { - expect(filter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'Staging_build_linux task')), true); - expect(filter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'staging_build_mac task')), true); - expect(filter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'Linux_android task')), true); - expect(filter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'linux_android task')), true); - } - }); - - test('staging filter staging tasks', () { - final List filters = [ - TaskGridFilter()..showStaging = false, - ]; - for (final TaskGridFilter filter in filters) { - expect(filter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'Staging_build_linux task')), false); - expect(filter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'staging_build_mac task')), false); - expect(filter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'Linux_android task')), true); - expect(filter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'linux_android task')), true); - } - }); - - test('staging filter name matches', () { - final List filters = [ - TaskGridFilter()..showStaging = false, - ]; - for (final TaskGridFilter filter in filters) { - expect(filter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'Staging_build_linux task')), false); - expect(filter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'Staging_build_mac task')), false); - expect(filter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'Linux_android staging_build')), true); - expect( - filter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'linux_android_staging_build_linux task')), - true, - ); - } - }); - - test('matches task name simple substring', () { - final List filters = [ - TaskGridFilter.fromMap({'taskFilter': 'foo'}), - TaskGridFilter()..taskFilter = RegExp('foo'), - ]; - expect(filters[0], filters[1]); - for (final TaskGridFilter filter in filters) { - expect(filter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'foo')), true); - expect(filter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'Foo')), true); - expect(filter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'blah foo blah')), true); - expect(filter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'fo')), false); - } - }); - - test('matches task name simple substring case insensitive', () { - final List filters = [ - TaskGridFilter.fromMap({'taskFilter': 'foo'}), - TaskGridFilter()..taskFilter = RegExp('foo'), - TaskGridFilter()..taskFilter = RegExp('FOO'), - ]; - expect(filters[0], filters[1]); - for (final TaskGridFilter filter in filters) { - expect(filter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'foo')), true); - expect(filter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'Foo')), true); - expect(filter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'blah fOO blah')), true); - expect(filter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'fo')), false); - } - }); - - test('matches task name regexp', () { - final List filters = [ - TaskGridFilter.fromMap({'taskFilter': '.*[ab][cd]\$'}), - TaskGridFilter()..taskFilter = RegExp('.*[ab][cd]\$'), - ]; - expect(filters[0], filters[1]); - for (final TaskGridFilter filter in filters) { - expect(filter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'z bc')), true); - expect(filter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'z bc z')), false); - expect(filter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'z b c')), false); - expect(filter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'foo')), false); - } - }); - - void testStage({ - required String taskName, - required String fieldName, - required TaskGridFilter trueFilter, - required TaskGridFilter falseFilter, - }) { - final TaskGridFilter trueFilterMap = TaskGridFilter.fromMap({fieldName: 'true'}); - final TaskGridFilter falseFilterMap = TaskGridFilter.fromMap({fieldName: 'false'}); - - expect(trueFilter, trueFilterMap); - expect(trueFilter, isNot(equals(falseFilterMap))); - expect(trueFilter, isNot(equals(falseFilter))); - expect(falseFilter, falseFilterMap); - expect(falseFilter, isNot(equals(trueFilterMap))); - expect(falseFilter, isNot(equals(trueFilter))); - - expect(trueFilter.matchesTask(QualifiedTask.fromTask(Task()..builderName = taskName)), true); - expect(trueFilterMap.matchesTask(QualifiedTask.fromTask(Task()..builderName = taskName)), true); - - expect(falseFilter.matchesTask(QualifiedTask.fromTask(Task()..builderName = taskName)), false); - expect(falseFilterMap.matchesTask(QualifiedTask.fromTask(Task()..builderName = taskName)), false); - } - - const Map showOSs = { - 'showMac': 'Mac', - 'showWindows': 'Windows', - 'showiOS': 'ios', - 'showLinux': 'Linux', - 'showAndroid': 'Android', - }; - for (MapEntry os in showOSs.entries) { - test('matches ${os.value} stage', () { - testStage( - taskName: os.value, - fieldName: os.key, - trueFilter: TaskGridFilter.fromMap({os.key: 'true'}), - falseFilter: TaskGridFilter.fromMap({os.key: 'false'}), - ); - }); - } - - test('matches ios and android filters logic', () { - final TaskGridFilter iosMacFilter = TaskGridFilter.fromMap({'showMac': 'false', 'showiOS': 'true'}); - final TaskGridFilter macIosFilter = TaskGridFilter.fromMap({'showMac': 'true', 'showiOS': 'false'}); - final TaskGridFilter macIosBothTrueFilter = - TaskGridFilter.fromMap({'showMac': 'true', 'showiOS': 'true'}); - - final TaskGridFilter androidLinuxFilter = - TaskGridFilter.fromMap({'showLinux': 'false', 'showAndroid': 'true'}); - final TaskGridFilter linuxAndroidFilter = - TaskGridFilter.fromMap({'showLinux': 'true', 'showAndroid': 'false'}); - final TaskGridFilter linuxAndroidBothTrueFilter = - TaskGridFilter.fromMap({'showLinux': 'true', 'showAndroid': 'true'}); - final TaskGridFilter androidFalseFilter = TaskGridFilter.fromMap({'showAndroid': 'false'}); - - expect(iosMacFilter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'Mac_ios')), true); - expect(iosMacFilter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'Mac')), false); - expect(macIosFilter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'Mac_ios')), false); - expect(macIosFilter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'Mac')), true); - expect(macIosBothTrueFilter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'Mac_ios')), true); - expect(macIosBothTrueFilter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'Mac')), true); - expect(androidLinuxFilter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'Linux_android')), true); - expect(androidLinuxFilter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'Linux')), false); - expect(linuxAndroidFilter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'Linux_android')), false); - expect(linuxAndroidFilter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'Linux')), true); - expect(linuxAndroidBothTrueFilter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'Linux_android')), true); - expect(linuxAndroidBothTrueFilter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'Linux_android')), true); - expect(androidLinuxFilter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'Windows_android')), true); - expect(androidFalseFilter.matchesTask(QualifiedTask.fromTask(Task()..builderName = 'Anything_android')), false); - }); - - test('matches author name simple substring', () { - final List filters = [ - TaskGridFilter.fromMap({'authorFilter': 'foo'}), - TaskGridFilter()..authorFilter = RegExp('foo'), - ]; - expect(filters[0], filters[1]); - for (final TaskGridFilter filter in filters) { - expect(filter.matchesCommit(CommitStatus()..commit = (Commit()..author = 'foo')), true); - expect(filter.matchesCommit(CommitStatus()..commit = (Commit()..author = 'blah foo blah')), true); - expect(filter.matchesCommit(CommitStatus()..commit = (Commit()..author = 'fo')), false); - } - }); - - test('matches author name regexp', () { - final List filters = [ - TaskGridFilter.fromMap({'authorFilter': '.*[ab][cd]\$'}), - TaskGridFilter()..authorFilter = RegExp('.*[ab][cd]\$'), - ]; - expect(filters[0], filters[1]); - for (final TaskGridFilter filter in filters) { - expect(filter.matchesCommit(CommitStatus()..commit = (Commit()..author = 'z bc')), true); - expect(filter.matchesCommit(CommitStatus()..commit = (Commit()..author = 'z bc z')), false); - expect(filter.matchesCommit(CommitStatus()..commit = (Commit()..author = 'z b c')), false); - expect(filter.matchesCommit(CommitStatus()..commit = (Commit()..author = 'foo')), false); - } - }); - - test('matches commit message simple substring', () { - final List filters = [ - TaskGridFilter.fromMap({'messageFilter': 'foo'}), - TaskGridFilter()..messageFilter = RegExp('foo'), - ]; - expect(filters[0], filters[1]); - for (final TaskGridFilter filter in filters) { - expect(filter.matchesCommit(CommitStatus()..commit = (Commit()..message = 'foo')), true); - expect(filter.matchesCommit(CommitStatus()..commit = (Commit()..message = 'blah foo blah')), true); - expect(filter.matchesCommit(CommitStatus()..commit = (Commit()..message = 'fo')), false); - } - }); - - test('matches commit message regexp', () { - final List filters = [ - TaskGridFilter.fromMap({'messageFilter': '.*[ab][cd]\$'}), - TaskGridFilter()..messageFilter = RegExp('.*[ab][cd]\$'), - ]; - expect(filters[0], filters[1]); - for (final TaskGridFilter filter in filters) { - expect(filter.matchesCommit(CommitStatus()..commit = (Commit()..message = 'z bc')), true); - expect(filter.matchesCommit(CommitStatus()..commit = (Commit()..message = 'z bc z')), false); - expect(filter.matchesCommit(CommitStatus()..commit = (Commit()..message = 'z b c')), false); - expect(filter.matchesCommit(CommitStatus()..commit = (Commit()..message = 'foo')), false); - } - }); - - test('matches commit sha simple substring', () { - final List filters = [ - TaskGridFilter.fromMap({'hashFilter': 'foo'}), - TaskGridFilter()..hashFilter = RegExp('foo'), - ]; - expect(filters[0], filters[1]); - for (final TaskGridFilter filter in filters) { - expect(filter.matchesCommit(CommitStatus()..commit = (Commit()..sha = 'foo')), true); - expect(filter.matchesCommit(CommitStatus()..commit = (Commit()..sha = 'blah foo blah')), true); - expect(filter.matchesCommit(CommitStatus()..commit = (Commit()..sha = 'fo')), false); - } - }); - - test('matches commit sha regexp', () { - final List filters = [ - TaskGridFilter.fromMap({'hashFilter': '.*[ab][cd]\$'}), - TaskGridFilter()..hashFilter = RegExp('.*[ab][cd]\$'), - ]; - expect(filters[0], filters[1]); - for (final TaskGridFilter filter in filters) { - expect(filter.matchesCommit(CommitStatus()..commit = (Commit()..sha = 'z bc')), true); - expect(filter.matchesCommit(CommitStatus()..commit = (Commit()..sha = 'z bc z')), false); - expect(filter.matchesCommit(CommitStatus()..commit = (Commit()..sha = 'z b c')), false); - expect(filter.matchesCommit(CommitStatus()..commit = (Commit()..sha = 'foo')), false); - } - }); -} diff --git a/dashboard/test/service/appengine_cocoon_test.dart b/dashboard/test/service/appengine_cocoon_test.dart deleted file mode 100644 index e7a6ca745..000000000 --- a/dashboard/test/service/appengine_cocoon_test.dart +++ /dev/null @@ -1,353 +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:flutter/foundation.dart' show kIsWeb; -import 'package:flutter_dashboard/logic/qualified_task.dart'; -import 'package:flutter_dashboard/model/branch.pb.dart'; -import 'package:flutter_dashboard/model/build_status_response.pb.dart'; -import 'package:flutter_dashboard/model/commit.pb.dart'; -import 'package:flutter_dashboard/model/commit_status.pb.dart'; -import 'package:flutter_dashboard/model/key.pb.dart'; -import 'package:flutter_dashboard/model/task.pb.dart'; -import 'package:flutter_dashboard/service/appengine_cocoon.dart'; -import 'package:flutter_dashboard/service/cocoon.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:http/http.dart' show Client, Request, Response; -import 'package:http/testing.dart'; -import 'package:mockito/mockito.dart'; - -import '../utils/appengine_cocoon_test_data.dart'; - -void main() { - group('AppEngine CocoonService fetchCommitStatus', () { - late AppEngineCocoonService service; - - setUp(() async { - service = AppEngineCocoonService( - client: MockClient((Request request) async { - return Response(luciJsonGetStatsResponse, 200); - }), - ); - }); - - test('should return CocoonResponse>', () { - expect( - service.fetchCommitStatuses(repo: 'engine'), - const TypeMatcher>>>(), - ); - }); - - test('should return expected List', () async { - final CocoonResponse> statuses = await service.fetchCommitStatuses(repo: 'engine'); - - final CommitStatus expectedStatus = CommitStatus() - ..branch = 'master' - ..commit = (Commit() - ..timestamp = Int64(123456789) - ..key = (RootKey()..child = (Key()..name = 'iamatestkey')) - ..sha = 'ShaShankHash' - ..author = 'ShaSha' - ..authorAvatarUrl = 'https://flutter.dev' - ..repository = 'flutter/cocoon' - ..branch = 'master') - ..tasks.add( - Task() - ..key = (RootKey()..child = (Key()..name = 'taskKey1')) - ..createTimestamp = Int64(1569353940885) - ..startTimestamp = Int64(1569354594672) - ..endTimestamp = Int64(1569354700642) - ..name = 'linux' - ..attempts = 1 - ..isFlaky = false - ..timeoutInMinutes = 0 - ..reason = '' - ..requiredCapabilities.add('[linux]') - ..reservedForAgentId = '' - ..stageName = 'chromebot' - ..status = 'Succeeded' - ..isTestFlaky = false - ..buildNumberList = '123' - ..builderName = 'Linux' - ..luciBucket = 'luci.flutter.try', - ); - - expect(statuses.data!.length, 1); - expect(statuses.data!.first, expectedStatus); - }); - - test('should have error if given non-200 response', () async { - service = AppEngineCocoonService(client: MockClient((Request request) async => Response('', 404))); - - final CocoonResponse> response = await service.fetchCommitStatuses(repo: 'engine'); - expect(response.error, isNotNull); - }); - - test('should have error if given bad response', () async { - service = AppEngineCocoonService(client: MockClient((Request request) async => Response('bad', 200))); - - final CocoonResponse> response = await service.fetchCommitStatuses(repo: 'engine'); - expect(response.error, isNotNull); - }); - }); - - group('AppEngine CocoonService fetchTreeBuildStatus', () { - late AppEngineCocoonService service; - - setUp(() async { - service = AppEngineCocoonService( - client: MockClient((Request request) async { - return Response(jsonBuildStatusTrueResponse, 200); - }), - ); - }); - - test('should return CocoonResponse', () { - expect( - service.fetchTreeBuildStatus(repo: 'engine'), - const TypeMatcher>>(), - ); - }); - - test('data should be true when given Succeeded', () async { - final CocoonResponse treeBuildStatus = await service.fetchTreeBuildStatus(repo: 'engine'); - - expect(treeBuildStatus.data!.buildStatus, EnumBuildStatus.success); - }); - - test('data should be false when given Failed', () async { - service = AppEngineCocoonService( - client: MockClient((Request request) async { - return Response(jsonBuildStatusFalseResponse, 200); - }), - ); - final CocoonResponse treeBuildStatus = await service.fetchTreeBuildStatus(repo: 'engine'); - - expect(treeBuildStatus.data!.buildStatus, EnumBuildStatus.failure); - }); - - test('should have error if given non-200 response', () async { - service = AppEngineCocoonService(client: MockClient((Request request) async => Response('', 404))); - - final CocoonResponse response = await service.fetchTreeBuildStatus(repo: 'engine'); - expect(response.error, isNotNull); - }); - - test('should have error if given bad response', () async { - service = AppEngineCocoonService(client: MockClient((Request request) async => Response('bad', 200))); - - final CocoonResponse response = await service.fetchTreeBuildStatus(repo: 'engine'); - expect(response.error, isNotNull); - }); - }); - - group('AppEngine CocoonService rerun task', () { - late AppEngineCocoonService service; - late Task task; - - setUp(() { - service = AppEngineCocoonService( - client: MockClient((Request request) async { - return Response('', 200); - }), - ); - task = Task() - ..key = RootKey() - ..stageName = StageName.luci; - }); - - test('should return true if request succeeds', () async { - final CocoonResponse response = await service.rerunTask(task, 'fakeAccessToken', 'engine'); - expect(response.error, isNull); - }); - - test('should set error in response if task key is null', () async { - final CocoonResponse response = await service.rerunTask(task, null, 'engine'); - expect( - response.error, - allOf([ - isNotNull, - contains('Sign in to trigger reruns'), - ]), - ); - }); - - test('should set error in response if bad status code is returned', () async { - service = AppEngineCocoonService( - client: MockClient((Request request) async { - return Response('internal server error', 500); - }), - ); - - final CocoonResponse response = await service.rerunTask(task, 'fakeAccessToken', 'engine'); - expect( - response.error, - allOf([ - isNotNull, - contains('HTTP Code: 500, internal server error'), - ]), - ); - }); - }); - - group('AppEngine CocoonService refresh github commits', () { - late AppEngineCocoonService service; - - setUp(() { - service = AppEngineCocoonService( - client: MockClient((Request request) async { - return Response('', 200); - }), - ); - }); - - test('should return true if request succeeds', () async { - expect(await service.vacuumGitHubCommits('fakeIdToken'), true); - }); - - test('should return false if request failed', () async { - service = AppEngineCocoonService( - client: MockClient((Request request) async { - return Response('', 500); - }), - ); - expect(await service.vacuumGitHubCommits('fakeIdToken'), false); - }); - }); - - group('AppEngine CocoonService fetchFlutterBranches', () { - late AppEngineCocoonService service; - - setUp(() async { - service = AppEngineCocoonService( - client: MockClient((Request request) async { - return Response(jsonGetBranchesResponse, 200); - }), - ); - }); - - test('should return CocoonResponse>', () { - expect(service.fetchFlutterBranches(), const TypeMatcher>>>()); - }); - - test('data should be expected list of branches', () async { - final CocoonResponse> branches = await service.fetchFlutterBranches(); - - expect(branches.error, isNull); - expect(branches.data!.length, 4); - expect( - branches.data, - containsAll([ - Branch() - ..branch = 'flutter-3.13-candidate.0' - ..channel = 'stable', - Branch() - ..branch = 'flutter-3.14-candidate.0' - ..channel = 'beta', - Branch() - ..branch = 'flutter-3.15-candidate.5' - ..channel = 'dev', - Branch() - ..branch = 'master' - ..channel = 'HEAD', - ]), - ); - }); - - test('should have error if given non-200 response', () async { - service = AppEngineCocoonService(client: MockClient((Request request) async => Response('', 404))); - - final CocoonResponse> response = await service.fetchFlutterBranches(); - expect(response.error, isNotNull); - }); - - test('should have error if given bad response', () async { - service = AppEngineCocoonService(client: MockClient((Request request) async => Response('bad', 200))); - - final CocoonResponse> response = await service.fetchFlutterBranches(); - expect(response.error, isNotNull); - }); - }); - - group('AppEngine CocoonService fetchRepos', () { - late AppEngineCocoonService service; - - setUp(() async { - service = AppEngineCocoonService( - client: MockClient((Request request) async { - return Response(jsonGetReposResponse, 200); - }), - ); - }); - - test('data should be expected list of branches', () async { - final CocoonResponse> repos = await service.fetchRepos(); - - expect(repos.data, [ - 'flutter', - 'cocoon', - 'engine', - ]); - }); - - test('should have error if given non-200 response', () async { - service = AppEngineCocoonService(client: MockClient((Request request) async => Response('', 404))); - - final CocoonResponse> response = await service.fetchRepos(); - expect(response.error, isNotNull); - }); - - test('should have error if given bad response', () async { - service = AppEngineCocoonService(client: MockClient((Request request) async => Response('bad', 200))); - - final CocoonResponse> response = await service.fetchRepos(); - expect(response.error, isNotNull); - }); - }); - - group('AppEngine CocoonService apiEndpoint', () { - final AppEngineCocoonService service = AppEngineCocoonService( - client: MockClient((Request request) async { - return Response('{"Token": "abc123"}', 200); - }), - ); - - test('handles url suffix', () { - expect(service.apiEndpoint('/test').toString(), '$baseApiUrl/test'); - }); - - test('single query parameter', () { - expect( - service.apiEndpoint('/test', queryParameters: {'key': 'value'}).toString(), - '$baseApiUrl/test?key=value', - ); - }); - - test('multiple query parameters', () { - expect( - service.apiEndpoint('/test', queryParameters: {'key': 'value', 'another': 'test'}).toString(), - '$baseApiUrl/test?key=value&another=test', - ); - }); - - test('query parameter with null value', () { - expect( - service.apiEndpoint('/test', queryParameters: {'key': null}).toString(), - '$baseApiUrl/test?key', - ); - }); - - /// This test requires runs on different platforms. - test('should query correct endpoint whether web or mobile', () { - final String uri = service.apiEndpoint('/test', queryParameters: {'key': null}).toString(); - if (kIsWeb) { - expect(uri, '/test?key'); - } else { - expect(uri, '$baseApiUrl/test?key'); - } - }); - }); -} - -class MockHttpClient extends Mock implements Client {} diff --git a/dashboard/test/service/google_authentication_test.dart b/dashboard/test/service/google_authentication_test.dart deleted file mode 100644 index a5884a7e6..000000000 --- a/dashboard/test/service/google_authentication_test.dart +++ /dev/null @@ -1,141 +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:flutter_dashboard/service/google_authentication.dart'; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:google_sign_in/google_sign_in.dart'; -import 'package:mockito/mockito.dart'; - -import '../utils/fake_google_account.dart'; -import '../utils/mocks.dart'; - -void main() { - group('GoogleSignInService not signed in', () { - late GoogleSignInService authService; - GoogleSignIn? mockSignIn; - - setUp(() { - mockSignIn = MockGoogleSignIn(); - when(mockSignIn!.onCurrentUserChanged).thenAnswer((_) => const Stream.empty()); - when(mockSignIn!.isSignedIn()).thenAnswer((_) => Future.value(false)); - when(mockSignIn!.signInSilently()).thenAnswer((_) => Future.value(null)); - authService = GoogleSignInService(googleSignIn: mockSignIn); - }); - - tearDown(() { - clearInteractions(mockSignIn); - }); - - test('not authenticated', () async { - expect(authService.isAuthenticated, false); - }); - - test('no user information', () { - expect(authService.user, null); - }); - - test('sign in silently called', () async { - verify(mockSignIn!.signInSilently()).called(1); - }); - }); - - group('GoogleSignInService sign in', () { - late GoogleSignInService authService; - late GoogleSignIn mockSignIn; - late StreamController userChanged; - - final GoogleSignInAccount testAccount = FakeGoogleSignInAccount(); - - // Pushes a change to the userChanged Stream controller that we can await for it to propagate. - Future pushUserChanged(GoogleSignInAccount? account) { - userChanged.add(testAccount); - // Let the change to the stream propagate to the object... - return Future.delayed(Duration.zero); - } - - setUp(() { - userChanged = StreamController.broadcast(); - - mockSignIn = MockGoogleSignIn(); - when(mockSignIn.signIn()).thenAnswer((_) => Future.value(testAccount)); - when(mockSignIn.signInSilently()).thenAnswer((_) => Future.value(testAccount)); - when(mockSignIn.currentUser).thenReturn(testAccount); - when(mockSignIn.isSignedIn()).thenAnswer((_) => Future.value(true)); - when(mockSignIn.onCurrentUserChanged).thenAnswer((_) => userChanged.stream); - - authService = GoogleSignInService(googleSignIn: mockSignIn); - }); - - test('is authenticated after sign in from Google Sign In button', () async { - await pushUserChanged(testAccount); - - expect(authService.isAuthenticated, isTrue); - expect(authService.user, testAccount); - }); - - test('there is user information after successful sign in', () async { - await pushUserChanged(testAccount); - - expect(authService.user, isNotNull); - expect(authService.user!.displayName, 'Dr. Test'); - expect(authService.user!.email, 'test@flutter.dev'); - expect(authService.user!.id, 'test123'); - expect( - authService.user!.photoUrl, - 'https://lh3.googleusercontent.com/-ukEAtRyRhw8/AAAAAAAAAAI/AAAAAAAAAAA/ACHi3rfhID9XACtdb9q_xK43VSXQvBV11Q.CMID', - ); - }); - - test('signIn method also works, but should be deprecated!', () async { - await authService.signIn(); - - expect(authService.isAuthenticated, true); - expect(authService.user, testAccount); - }); - - test('id token available with logged in user', () async { - final GoogleSignInAccount testAccountWithAuthentication = FakeGoogleSignInAccount() - ..authentication = Future.value(FakeGoogleSignInAuthentication()); - authService.user = testAccountWithAuthentication; - - expect(await authService.idToken, 'id123'); - }); - - test('is not authenticated after failure in sign in', () async { - when(mockSignIn.signInSilently()).thenAnswer((_) => Future.value(null)); - when(mockSignIn.signIn()).thenAnswer((_) => Future.value(null)); - - await authService.signIn(); - - expect(authService.user, null); - }); - - test('clearUser removes the user without calling signOut', () async { - await pushUserChanged(testAccount); - - expect(authService.isAuthenticated, isTrue); - expect(authService.user, testAccount); - - await authService.clearUser(); - - expect(authService.isAuthenticated, isFalse); - expect(authService.user, isNull); - verifyNever(mockSignIn.signOut()); - }); - }); -} - -class FakeGoogleSignInAuthentication implements GoogleSignInAuthentication { - @override - String get accessToken => 'access123'; - - @override - String get idToken => 'id123'; - - @override - String get serverAuthCode => 'serverAuth123'; -} diff --git a/dashboard/test/state/build_test.dart b/dashboard/test/state/build_test.dart deleted file mode 100644 index a838f24d5..000000000 --- a/dashboard/test/state/build_test.dart +++ /dev/null @@ -1,497 +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:flutter_app_icons/flutter_app_icons_platform_interface.dart'; -import 'package:flutter_dashboard/model/branch.pb.dart'; -import 'package:flutter_dashboard/model/build_status_response.pb.dart'; -import 'package:flutter_dashboard/model/commit.pb.dart'; -import 'package:flutter_dashboard/model/commit_status.pb.dart'; -import 'package:flutter_dashboard/model/key.pb.dart'; -import 'package:flutter_dashboard/model/task.pb.dart'; -import 'package:flutter_dashboard/service/cocoon.dart'; -import 'package:flutter_dashboard/service/google_authentication.dart'; -import 'package:flutter_dashboard/state/build.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:google_sign_in/google_sign_in.dart'; -import 'package:mockito/mockito.dart'; - -import '../utils/fake_flutter_app_icons.dart'; -import '../utils/mocks.dart'; -import '../utils/output.dart'; - -void main() { - const String defaultBranch = 'master'; - - group('BuildState', () { - late MockCocoonService mockCocoonService; - late CommitStatus setupCommitStatus; - - setUp(() { - mockCocoonService = MockCocoonService(); - setupCommitStatus = _createCommitStatus('setup'); - - when(mockCocoonService.fetchCommitStatuses(branch: anyNamed('branch'), repo: anyNamed('repo'))) - .thenAnswer((dynamic _) async => CocoonResponse>.data([setupCommitStatus])); - when(mockCocoonService.fetchTreeBuildStatus(branch: anyNamed('branch'), repo: anyNamed('repo'))).thenAnswer( - (_) async => - CocoonResponse.data(BuildStatusResponse()..buildStatus = EnumBuildStatus.success), - ); - when(mockCocoonService.fetchRepos()) - .thenAnswer((_) async => const CocoonResponse>.data(['flutter'])); - when(mockCocoonService.fetchFlutterBranches()).thenAnswer( - (_) async => CocoonResponse>.data([ - Branch() - ..branch = defaultBranch - ..repository = 'flutter', - ]), - ); - - FlutterAppIconsPlatform.instance = FakeFlutterAppIcons(); - }); - - tearDown(() { - clearInteractions(mockCocoonService); - }); - - testWidgets('start calls fetch branches', (WidgetTester tester) async { - final BuildState buildState = BuildState( - authService: MockGoogleSignInService(), - cocoonService: mockCocoonService, - ); - void listener() {} - buildState.addListener(listener); - - // startFetching immediately starts fetching results - verify(await mockCocoonService.fetchFlutterBranches()).called(1); - - buildState.dispose(); - }); - - testWidgets('timer should periodically fetch updates', (WidgetTester tester) async { - final BuildState buildState = BuildState( - authService: MockGoogleSignInService(), - cocoonService: mockCocoonService, - ); - verifyNever(mockCocoonService.fetchCommitStatuses(branch: anyNamed('branch'), repo: anyNamed('repo'))); - - void listener() {} - buildState.addListener(listener); - - // startFetching immediately starts fetching results - verify(await mockCocoonService.fetchCommitStatuses(branch: defaultBranch, repo: 'flutter')).called(1); - - verifyNever(mockCocoonService.fetchCommitStatuses(branch: anyNamed('branch'), repo: anyNamed('repo'))); - await tester.pump(buildState.refreshRate! * 2); - verify(await mockCocoonService.fetchCommitStatuses(branch: defaultBranch, repo: 'flutter')).called(2); - - buildState.dispose(); - }); - - testWidgets('updateCurrentRepoBranch should make old updates stale', (WidgetTester tester) async { - final BuildState buildState = BuildState( - authService: MockGoogleSignInService(), - cocoonService: mockCocoonService, - ); - - verifyNever(mockCocoonService.fetchCommitStatuses(branch: anyNamed('branch'), repo: anyNamed('repo'))); - expect(buildState.statuses, isEmpty); - - void listener() {} - // This invokes startFetchUpdates - buildState.addListener(listener); - - // startFetching immediately starts fetching results (and returns fake data) - verify(await mockCocoonService.fetchCommitStatuses(branch: defaultBranch, repo: 'flutter')).called(1); - verifyNever(mockCocoonService.fetchCommitStatuses(branch: 'main', repo: 'cocoon')); - expect(buildState.statuses, isNotEmpty); - // Start another Timer.periodic async call - await tester.pump(); - // Change the repo to Cocoon while a timer is set, and Cocoon is not expected to return data - buildState.updateCurrentRepoBranch('cocoon', 'main'); - expect(buildState.statuses, isEmpty); - await untilCalled(mockCocoonService.fetchCommitStatuses(branch: defaultBranch, repo: 'flutter')); - expect(buildState.statuses, isEmpty); - - buildState.dispose(); - }); - - test('multiple start updates should not change the timer', () { - final BuildState buildState = BuildState( - authService: MockGoogleSignInService(), - cocoonService: mockCocoonService, - ); - void listener1() {} - buildState.addListener(listener1); - - // Another listener shouldn't change the timer. - final Timer? refreshTimer = buildState.refreshTimer; - void listener2() {} - buildState.addListener(listener2); - expect(buildState.refreshTimer, equals(refreshTimer)); - - // Removing a listener shouldn't change the timer. - buildState.removeListener(listener1); - expect(buildState.refreshTimer, equals(refreshTimer)); - - // Removing both listeners should cancel the timer. - buildState.removeListener(listener2); - expect(buildState.refreshTimer, isNull); - - // A new listener now should change the timer. - buildState.addListener(listener1); - expect(buildState.refreshTimer, isNot(isNull)); - expect(buildState.refreshTimer, isNot(equals(refreshTimer))); - }); - - testWidgets('statuses error should not delete previous statuses data', (WidgetTester tester) async { - String? lastError; - final BuildState buildState = BuildState( - authService: MockGoogleSignInService(), - cocoonService: mockCocoonService, - )..errors.addListener((String message) => lastError = message); - verifyNever(mockCocoonService.fetchTreeBuildStatus(branch: anyNamed('branch'), repo: anyNamed('repo'))); - verifyNever(mockCocoonService.fetchCommitStatuses(branch: anyNamed('branch'), repo: anyNamed('repo'))); - void listener() {} - buildState.addListener(listener); - verify(mockCocoonService.fetchTreeBuildStatus(branch: defaultBranch, repo: 'flutter')).called(1); - verify(mockCocoonService.fetchCommitStatuses(branch: defaultBranch, repo: 'flutter')).called(1); - await tester.pump(); - final List originalData = buildState.statuses; - verifyNever(mockCocoonService.fetchTreeBuildStatus(branch: anyNamed('branch'), repo: anyNamed('repo'))); - verifyNever(mockCocoonService.fetchCommitStatuses(branch: anyNamed('branch'), repo: anyNamed('repo'))); - - when(mockCocoonService.fetchCommitStatuses(branch: defaultBranch, repo: 'flutter')).thenAnswer( - (_) => - Future>>.value(const CocoonResponse>.error('error')), - ); - await checkOutput( - block: () async { - await tester.pump(buildState.refreshRate); - }, - output: [ - 'An error occurred fetching build statuses from Cocoon: error', - ], - ); - verify(await mockCocoonService.fetchTreeBuildStatus(branch: defaultBranch, repo: 'flutter')).called(1); - verify(await mockCocoonService.fetchCommitStatuses(branch: defaultBranch, repo: 'flutter')).called(1); - - expect(buildState.statuses, originalData); - expect(lastError, startsWith(BuildState.errorMessageFetchingStatuses)); - - buildState.dispose(); - }); - - testWidgets('build status error should not delete previous build status data', (WidgetTester tester) async { - String? lastError; - final BuildState buildState = BuildState( - authService: MockGoogleSignInService(), - cocoonService: mockCocoonService, - )..errors.addListener((String message) => lastError = message); - verifyNever(mockCocoonService.fetchTreeBuildStatus(branch: anyNamed('branch'), repo: anyNamed('repo'))); - void listener() {} - buildState.addListener(listener); - - await tester.pump(); - verify(mockCocoonService.fetchTreeBuildStatus(branch: defaultBranch, repo: 'flutter')).called(1); - final bool? originalData = buildState.isTreeBuilding; - verifyNever(mockCocoonService.fetchTreeBuildStatus(branch: anyNamed('branch'), repo: anyNamed('repo'))); - verifyNever(mockCocoonService.fetchTreeBuildStatus(branch: defaultBranch, repo: 'flutter')); - - when(mockCocoonService.fetchTreeBuildStatus(branch: defaultBranch, repo: 'flutter')).thenAnswer( - (_) => - Future>.value(const CocoonResponse.error('error')), - ); - await checkOutput( - block: () async { - await tester.pump(buildState.refreshRate); - }, - output: [ - 'An error occurred fetching tree status from Cocoon: error', - ], - ); - verify(await mockCocoonService.fetchTreeBuildStatus(branch: defaultBranch, repo: 'flutter')).called(1); - - expect(buildState.isTreeBuilding, originalData); - expect(lastError, startsWith(BuildState.errorMessageFetchingTreeStatus)); - - buildState.dispose(); - }); - - testWidgets('fetch more commit statuses appends', (WidgetTester tester) async { - final BuildState buildState = BuildState( - authService: MockGoogleSignInService(), - cocoonService: mockCocoonService, - ); - void listener() {} - buildState.addListener(listener); - - await untilCalled(mockCocoonService.fetchCommitStatuses(branch: anyNamed('branch'), repo: anyNamed('repo'))); - - expect(buildState.statuses, [setupCommitStatus]); - - final CommitStatus statusA = _createCommitStatus('A'); - when( - mockCocoonService.fetchCommitStatuses( - lastCommitStatus: captureThat(isNotNull, named: 'lastCommitStatus'), - branch: anyNamed('branch'), - repo: anyNamed('repo'), - ), - ).thenAnswer((_) async => CocoonResponse>.data([statusA])); - - await buildState.fetchMoreCommitStatuses(); - - expect(buildState.statuses, [setupCommitStatus, statusA]); - - await tester.pump(buildState.refreshRate); - - expect(buildState.statuses, [setupCommitStatus, statusA]); - expect(buildState.moreStatusesExist, true); - - buildState.dispose(); - }); - - testWidgets('fetchMoreCommitStatuses returns empty stops fetching more', (WidgetTester tester) async { - final BuildState buildState = BuildState( - authService: MockGoogleSignInService(), - cocoonService: mockCocoonService, - ); - void listener() {} - buildState.addListener(listener); - - await untilCalled(mockCocoonService.fetchCommitStatuses(branch: anyNamed('branch'), repo: anyNamed('repo'))); - - expect(buildState.statuses, [setupCommitStatus]); - - when( - mockCocoonService.fetchCommitStatuses( - lastCommitStatus: captureThat(isNotNull, named: 'lastCommitStatus'), - branch: anyNamed('branch'), - repo: anyNamed('repo'), - ), - ).thenAnswer((_) async => const CocoonResponse>.data([])); - - await buildState.fetchMoreCommitStatuses(); - - expect(buildState.statuses, [setupCommitStatus]); - expect(buildState.moreStatusesExist, false); - - buildState.dispose(); - }); - - testWidgets('update branch resets build state data', (WidgetTester tester) async { - // Only return statuses when on master branch - when( - mockCocoonService.fetchCommitStatuses(branch: 'master', repo: 'flutter'), - ).thenAnswer( - (_) => Future>>.value( - CocoonResponse>.data([setupCommitStatus]), - ).then((CocoonResponse> value) => value), - ); - // Mark tree green on master, red on dev - when(mockCocoonService.fetchTreeBuildStatus(branch: 'master', repo: 'flutter')).thenAnswer( - (_) => Future>.value( - CocoonResponse.data(BuildStatusResponse()..buildStatus = EnumBuildStatus.success), - ), - ); - when(mockCocoonService.fetchTreeBuildStatus(branch: 'dev', repo: 'flutter')).thenAnswer( - (_) => Future>.value( - CocoonResponse.data( - BuildStatusResponse() - ..buildStatus = EnumBuildStatus.failure - ..failingTasks.addAll(['failing_task_1']), - ), - ), - ); - final BuildState buildState = BuildState( - authService: MockGoogleSignInService(), - cocoonService: mockCocoonService, - ); - void listener() {} - buildState.addListener(listener); - - await untilCalled(mockCocoonService.fetchCommitStatuses(branch: 'master', repo: 'flutter')); - expect(buildState.statuses, isNotEmpty); - expect(buildState.isTreeBuilding, isNotNull); - - // With mockito, the fetch requests for data will finish immediately - buildState.updateCurrentRepoBranch('flutter', 'dev'); - await tester.pump(); - - expect(buildState.statuses, isEmpty); - expect(buildState.isTreeBuilding, false); - expect(buildState.moreStatusesExist, true); - - buildState.dispose(); - }); - }); - - group('refreshGitHubCommits', () { - late MockCocoonService cocoonService; - late MockGoogleSignInService authService; - - setUp(() { - cocoonService = MockCocoonService(); - authService = MockGoogleSignInService(); - }); - - testWidgets('fails fast when !isAuthenticated', (_) async { - when(authService.isAuthenticated).thenReturn(false); - - final BuildState buildState = BuildState( - authService: authService, - cocoonService: cocoonService, - ); - - final bool result = await buildState.refreshGitHubCommits(); - - expect(result, isFalse); - verifyNever(cocoonService.vacuumGitHubCommits(any)); - }); - - testWidgets('clears user when vacuumGitHubCommits fails', (_) async { - const String idToken = 'id_token'; - when(authService.isAuthenticated).thenReturn(true); - when(authService.idToken).thenAnswer((_) async => idToken); - when(cocoonService.vacuumGitHubCommits(idToken)).thenAnswer((_) async => false); - - final BuildState buildState = BuildState( - authService: authService, - cocoonService: cocoonService, - ); - - final bool result = await buildState.refreshGitHubCommits(); - - expect(result, isFalse); - verify(authService.clearUser()).called(1); - }); - - testWidgets('returns true when vacuumGitHubCommits succeeds', (_) async { - const String idToken = 'id_token'; - when(authService.isAuthenticated).thenReturn(true); - when(authService.idToken).thenAnswer((_) async => idToken); - when(cocoonService.vacuumGitHubCommits(idToken)).thenAnswer((_) async => true); - - final BuildState buildState = BuildState( - authService: authService, - cocoonService: cocoonService, - ); - - final bool result = await buildState.refreshGitHubCommits(); - - expect(result, isTrue); - verifyNever(authService.clearUser()); - }); - }); - - group('rerunTask', () { - late MockCocoonService cocoonService; - late MockGoogleSignInService authService; - final Task task = Task(); - - setUp(() { - cocoonService = MockCocoonService(); - authService = MockGoogleSignInService(); - }); - - testWidgets('fails fast when !isAuthenticated', (_) async { - when(authService.isAuthenticated).thenReturn(false); - - final BuildState buildState = BuildState( - authService: authService, - cocoonService: cocoonService, - ); - - final bool result = await buildState.rerunTask(task); - - expect(result, isFalse); - verifyNever(cocoonService.rerunTask(any, any, any)); - }); - - testWidgets('clears user when rerunTask fails', (_) async { - const String idToken = 'id_token'; - when(authService.isAuthenticated).thenReturn(true); - when(authService.idToken).thenAnswer((_) async => idToken); - when(cocoonService.rerunTask(task, idToken, any)) - .thenAnswer((_) async => const CocoonResponse.error('failed!')); - - final BuildState buildState = BuildState( - authService: authService, - cocoonService: cocoonService, - ); - - final bool result = await buildState.rerunTask(task); - - expect(result, isFalse); - verify(authService.clearUser()).called(1); - }); - - testWidgets('returns true when rerunTask succeeds', (_) async { - const String idToken = 'id_token'; - when(authService.isAuthenticated).thenReturn(true); - when(authService.idToken).thenAnswer((_) async => idToken); - when(cocoonService.rerunTask(task, idToken, any)).thenAnswer((_) async => const CocoonResponse.data(true)); - - final BuildState buildState = BuildState( - authService: authService, - cocoonService: cocoonService, - ); - - final bool result = await buildState.rerunTask(task); - - expect(result, isTrue); - verifyNever(authService.clearUser()); - }); - }); - - testWidgets('sign in functions call notify listener', (WidgetTester tester) async { - final MockGoogleSignIn mockSignInPlugin = MockGoogleSignIn(); - when(mockSignInPlugin.signIn()).thenAnswer((_) => Future.value(null)); - when(mockSignInPlugin.signOut()).thenAnswer((_) => Future.value(null)); - when(mockSignInPlugin.signInSilently()).thenAnswer((_) => Future.value(null)); - when(mockSignInPlugin.onCurrentUserChanged).thenAnswer((_) => Stream.value(null)); - final MockCocoonService mockCocoonService = MockCocoonService(); - when(mockCocoonService.fetchFlutterBranches()).thenAnswer((_) => Completer>>().future); - when(mockCocoonService.fetchCommitStatuses(branch: anyNamed('branch'), repo: anyNamed('repo'))) - .thenAnswer((_) => Completer>>().future); - when(mockCocoonService.fetchRepos()).thenAnswer((_) => Completer>>().future); - when(mockCocoonService.fetchTreeBuildStatus(branch: anyNamed('branch'), repo: anyNamed('repo'))) - .thenAnswer((_) => Completer>().future); - final GoogleSignInService signInService = GoogleSignInService(googleSignIn: mockSignInPlugin); - final BuildState buildState = BuildState( - cocoonService: mockCocoonService, - authService: signInService, // TODO(ianh): Settle on one of these two for the whole codebase. - ); - - int callCount = 0; - buildState.addListener(() => callCount += 1); - - await tester.pump(const Duration(seconds: 5)); - expect(callCount, 1); - - await signInService.signIn(); - expect(callCount, 2); - - await signInService.signOut(); - expect(callCount, 3); - - buildState.dispose(); - }); -} - -CommitStatus _createCommitStatus( - String keyValue, { - String branch = 'master', - String repo = 'flutter', -}) { - return CommitStatus() - ..branch = branch - ..commit = (Commit() - // Author is set so we don't have to dig through all the nested fields - // while debugging - ..author = keyValue - ..repository = 'flutter/$repo' - ..key = (RootKey()..child = (Key()..name = keyValue))); -} diff --git a/dashboard/test/state/index_test.dart b/dashboard/test/state/index_test.dart deleted file mode 100644 index bf2ada8dd..000000000 --- a/dashboard/test/state/index_test.dart +++ /dev/null @@ -1,38 +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:flutter_dashboard/service/google_authentication.dart'; -import 'package:flutter_dashboard/state/index.dart'; - -import 'package:flutter_test/flutter_test.dart'; -import 'package:google_sign_in/google_sign_in.dart'; -import 'package:mockito/mockito.dart'; - -import '../utils/mocks.dart'; - -void main() { - testWidgets('IndexState sign in functions call notify listener', (WidgetTester tester) async { - final MockGoogleSignIn mockSignInPlugin = MockGoogleSignIn(); - when(mockSignInPlugin.onCurrentUserChanged).thenAnswer((_) => Stream.value(null)); - when(mockSignInPlugin.signIn()).thenAnswer((_) => Future.value(null)); - when(mockSignInPlugin.signOut()).thenAnswer((_) => Future.value(null)); - when(mockSignInPlugin.signInSilently()).thenAnswer((_) => Future.value(null)); - - final GoogleSignInService signInService = GoogleSignInService(googleSignIn: mockSignInPlugin); - final IndexState indexState = IndexState(authService: signInService); - - int callCount = 0; - indexState.addListener(() => callCount++); - - // notify listener is called during construction of the state - await tester.pump(const Duration(seconds: 5)); - expect(callCount, 1); - - await signInService.signIn(); - expect(callCount, 2); - - await signInService.signOut(); - expect(callCount, 3); - }); -} diff --git a/dashboard/test/utils/appengine_cocoon_test_data.dart b/dashboard/test/utils/appengine_cocoon_test_data.dart deleted file mode 100644 index 22cd8adf9..000000000 --- a/dashboard/test/utils/appengine_cocoon_test_data.dart +++ /dev/null @@ -1,91 +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. - -// This is based off data the Cocoon backend sends out from v1. -// It doesn't map directly to protos since the backend does -// not use protos yet. -const String luciJsonGetStatsResponse = ''' - { - "Statuses": [ - { - "Checklist": { - "Key": "iamatestkey", - "Checklist": { - "Branch": "master", - "FlutterRepositoryPath": "flutter/cocoon", - "CreateTimestamp": 123456789, - "Commit": { - "Sha": "ShaShankHash", - "Author": { - "Login": "ShaSha", - "avatar_url": "https://flutter.dev" - } - } - } - }, - "Stages": [ - { - "Name": "chromebot", - "Status": "Succeeded", - "Tasks": [ - { - "Key": "taskKey1", - "Task": { - "Attempts": 1, - "CreateTimestamp": 1569353940885, - "EndTimestamp": 1569354700642, - "Flaky": false, - "Name": "linux", - "Reason": "", - "RequiredCapabilities": ["linux"], - "ReservedForAgentID": "", - "StageName": "chromebot", - "StartTimestamp": 1569354594672, - "Status": "Succeeded", - "TimeoutInMinutes": 0, - "BuildNumberList": "123", - "BuilderName": "Linux", - "LuciBucket": "luci.flutter.try" - } - } - ] - } - ] - } - ] - } -'''; - -const String jsonGetBranchesResponse = '''[ - { - "branch":"flutter-3.13-candidate.0", - "name":"stable" - }, - { - "branch":"flutter-3.14-candidate.0", - "name":"beta" - }, - { - "branch":"flutter-3.15-candidate.5", - "name":"dev" - }, - { - "branch":"master", - "name":"HEAD" - } -]'''; - -const String jsonGetReposResponse = ''' - [ - "flutter", - "cocoon", - "engine" - ] -'''; - -const String jsonBuildStatusTrueResponse = '{"1":1}'; - -const String jsonBuildStatusFalseResponse = '{"1":2,"2":["failed_task_1"]}'; - -const String baseApiUrl = 'https://flutter-dashboard.appspot.com'; diff --git a/dashboard/test/utils/fake_build.dart b/dashboard/test/utils/fake_build.dart deleted file mode 100644 index 89500eb37..000000000 --- a/dashboard/test/utils/fake_build.dart +++ /dev/null @@ -1,112 +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:flutter/foundation.dart'; -import 'package:flutter_dashboard/logic/brooks.dart'; -import 'package:flutter_dashboard/model/branch.pb.dart'; -import 'package:flutter_dashboard/model/commit_status.pb.dart'; -import 'package:flutter_dashboard/model/task.pb.dart'; -import 'package:flutter_dashboard/service/cocoon.dart'; -import 'package:flutter_dashboard/service/google_authentication.dart'; -import 'package:flutter_dashboard/state/build.dart'; -import 'package:flutter_dashboard/widgets/task_overlay.dart'; - -import 'mocks.dart'; - -class FakeBuildState extends ChangeNotifier implements BuildState { - FakeBuildState({ - GoogleSignInService? authService, - CocoonService? cocoonService, - this.statuses = const [], - this.moreStatusesExist = true, - this.rerunTaskResult = false, - }) : authService = authService ?? MockGoogleSignInService(), - cocoonService = cocoonService ?? MockCocoonService(); - - @override - late GoogleSignInService authService; - - @override - final CocoonService cocoonService; - - @override - Timer? refreshTimer; - - @override - final ErrorSink errors = ErrorSink(); - - @override - bool? isTreeBuilding; - - @override - Duration? get refreshRate => const Duration(seconds: 30); - - @override - Future refreshGitHubCommits() async => false; - - @override - Future rerunTask(Task task) async { - if (!rerunTaskResult) { - errors.send(TaskOverlayContents.rerunErrorMessage); - return false; - } - return true; - } - - final bool rerunTaskResult; - - @override - final List statuses; - - @override - final bool moreStatusesExist; - - @override - Future? fetchMoreCommitStatuses() => null; - - @override - List get branches { - final List fakeBranches = []; - for (String repo in ['flutter', 'engine', 'cocoon']) { - fakeBranches.add( - Branch() - ..repository = repo - ..branch = defaultBranches[repo]!, - ); - fakeBranches.addAll( - [ - Branch() - ..repository = repo - ..branch = '$repo-release', - Branch() - ..repository = repo - ..branch = '$repo-release-very-long-name-that-should-be-truncated', - ], - ); - } - return fakeBranches; - } - - @override - String get currentBranch => _currentBranch; - String _currentBranch = 'master'; - - @override - List get failingTasks => []; - - @override - String get currentRepo => _currentRepo; - String _currentRepo = 'flutter'; - - @override - List get repos => ['flutter', 'engine', 'cocoon']; - - @override - Future updateCurrentRepoBranch(String repo, String branch) async { - _currentBranch = branch; - _currentRepo = repo; - } -} diff --git a/dashboard/test/utils/fake_flutter_app_icons.dart b/dashboard/test/utils/fake_flutter_app_icons.dart deleted file mode 100644 index b69f33db8..000000000 --- a/dashboard/test/utils/fake_flutter_app_icons.dart +++ /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. - -import 'package:flutter_app_icons/flutter_app_icons_platform_interface.dart'; - -class FakeFlutterAppIcons extends FlutterAppIconsPlatform { - @override - Future setIcon({ - required String icon, - String oldIcon = '', - String appleTouchIcon = '', - }) async { - return icon; - } -} diff --git a/dashboard/test/utils/fake_google_account.dart b/dashboard/test/utils/fake_google_account.dart deleted file mode 100644 index fac20ea81..000000000 --- a/dashboard/test/utils/fake_google_account.dart +++ /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. - -import 'package:google_sign_in/google_sign_in.dart'; - -class FakeGoogleSignInAccount implements GoogleSignInAccount { - @override - String get displayName => 'Dr. Test'; - - @override - String get email => 'test@flutter.dev'; - - @override - String get id => 'test123'; - - @override - String get photoUrl => - 'https://lh3.googleusercontent.com/-ukEAtRyRhw8/AAAAAAAAAAI/AAAAAAAAAAA/ACHi3rfhID9XACtdb9q_xK43VSXQvBV11Q.CMID'; - - @override - String get serverAuthCode => 'migration placeholder'; - - @override - Future> get authHeaders => Future>.value({}); - - @override - late final Future authentication; - - @override - Future clearAuthCache() => Future.value(null); - - @override - bool operator ==(Object other) { - final GoogleSignInAccount otherAccount = other as GoogleSignInAccount; - return email == otherAccount.email; - } - - @override - int get hashCode => email.hashCode; -} diff --git a/dashboard/test/utils/fake_index_state.dart b/dashboard/test/utils/fake_index_state.dart deleted file mode 100644 index dab729994..000000000 --- a/dashboard/test/utils/fake_index_state.dart +++ /dev/null @@ -1,20 +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:flutter/foundation.dart'; -import 'package:flutter_dashboard/logic/brooks.dart'; -import 'package:flutter_dashboard/service/google_authentication.dart'; -import 'package:flutter_dashboard/state/index.dart'; - -import 'mocks.dart'; - -class FakeIndexState extends ChangeNotifier implements IndexState { - FakeIndexState({GoogleSignInService? authService}) : authService = authService ?? MockGoogleSignInService(); - - @override - final GoogleSignInService authService; - - @override - final ErrorSink errors = ErrorSink(); -} diff --git a/dashboard/test/utils/fake_url_launcher.dart b/dashboard/test/utils/fake_url_launcher.dart deleted file mode 100644 index fddaf7e0d..000000000 --- a/dashboard/test/utils/fake_url_launcher.dart +++ /dev/null @@ -1,38 +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:url_launcher_platform_interface/link.dart'; -import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; - -class FakeUrlLauncher extends UrlLauncherPlatform { - final List launches = []; - - @override - Future canLaunch(String url) { - throw UnimplementedError('canLaunch() has not been implemented.'); - } - - @override - LinkDelegate? get linkDelegate => throw UnimplementedError('linkDelegate has not been implemented.'); - - @override - Future launch( - String url, { - required bool useSafariVC, - required bool useWebView, - required bool enableJavaScript, - required bool enableDomStorage, - required bool universalLinksOnly, - required Map headers, - String? webOnlyWindowName, - }) { - launches.add(url); - return Future.value(true); - } - - @override - Future closeWebView() { - throw UnimplementedError('closeWebView() has not been implemented.'); - } -} diff --git a/dashboard/test/utils/golden.dart b/dashboard/test/utils/golden.dart deleted file mode 100644 index bd0feab0d..000000000 --- a/dashboard/test/utils/golden.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 'dart:developer'; -import 'dart:io'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:path/path.dart' as path; - -const double _kGoldenDiffTolerance = 0.005; - -/// Wrapper function for golden tests in Cocoon. -/// -/// Ensures tests are only run on linux for consistency, and golden files are -/// stored in a goldens directory that is separate from the code. -Future expectGoldenMatches( - dynamic actual, - String goldenFileKey, { - String? reason, - dynamic skip = false, // true or a String -}) { - final String goldenPath = path.join('goldens', goldenFileKey); - goldenFileComparator = CocoonFileComparator( - path.join( - (goldenFileComparator as LocalFileComparator).basedir.toString(), - goldenFileKey, - ), - ); - return expectLater(actual, matchesGoldenFile(goldenPath), reason: reason, skip: skip || !Platform.isLinux); -} - -class CocoonFileComparator extends LocalFileComparator { - CocoonFileComparator(String testFile) : super(Uri.parse(testFile)); - - @override - Future compare(Uint8List imageBytes, Uri golden) async { - final ComparisonResult result = await GoldenFileComparator.compareLists( - imageBytes, - await getGoldenBytes(golden), - ); - - if (!result.passed && result.diffPercent > _kGoldenDiffTolerance) { - final String error = await generateFailureOutput(result, golden, basedir); - throw FlutterError(error); - } - if (!result.passed) { - log('A tolerable difference of ${result.diffPercent * 100}% was found when ' - 'comparing $golden.'); - } - return result.passed || result.diffPercent <= _kGoldenDiffTolerance; - } -} diff --git a/dashboard/test/utils/mocks.dart b/dashboard/test/utils/mocks.dart deleted file mode 100644 index c3f83715f..000000000 --- a/dashboard/test/utils/mocks.dart +++ /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. - -import 'package:flutter_dashboard/service/cocoon.dart'; -import 'package:flutter_dashboard/service/google_authentication.dart'; -import 'package:flutter_dashboard/state/build.dart'; -import 'package:google_sign_in/google_sign_in.dart'; -import 'package:http/http.dart'; -import 'package:mockito/annotations.dart'; - -export 'mocks.mocks.dart'; - -@GenerateMocks( - [ - Client, - CocoonService, - BuildState, - GoogleSignIn, - GoogleSignInService, - ], -) -void main() {} diff --git a/dashboard/test/utils/mocks.mocks.dart b/dashboard/test/utils/mocks.mocks.dart deleted file mode 100644 index 3f77168e3..000000000 --- a/dashboard/test/utils/mocks.mocks.dart +++ /dev/null @@ -1,795 +0,0 @@ -// Mocks generated by Mockito 5.4.1 from annotations -// in flutter_dashboard/test/utils/mocks.dart. -// Do not manually edit this file. - -// @dart=2.19 - -// ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i6; -import 'dart:convert' as _i7; -import 'dart:typed_data' as _i8; -import 'dart:ui' as _i14; - -import 'package:flutter_dashboard/logic/brooks.dart' as _i5; -import 'package:flutter_dashboard/model/branch.pb.dart' as _i11; -import 'package:flutter_dashboard/model/build_status_response.pb.dart' as _i10; -import 'package:flutter_dashboard/model/commit_status.pb.dart' as _i9; -import 'package:flutter_dashboard/model/task.pb.dart' as _i12; -import 'package:flutter_dashboard/service/cocoon.dart' as _i3; -import 'package:flutter_dashboard/service/google_authentication.dart' as _i4; -import 'package:flutter_dashboard/state/build.dart' as _i13; -import 'package:google_sign_in/google_sign_in.dart' as _i15; -import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart' as _i16; -import 'package:http/http.dart' as _i2; -import 'package:mockito/mockito.dart' as _i1; - -// ignore_for_file: type=lint -// ignore_for_file: avoid_redundant_argument_values -// ignore_for_file: avoid_setters_without_getters -// ignore_for_file: comment_references -// ignore_for_file: implementation_imports -// ignore_for_file: invalid_use_of_visible_for_testing_member -// ignore_for_file: prefer_const_constructors -// ignore_for_file: unnecessary_parenthesis -// ignore_for_file: camel_case_types -// ignore_for_file: subtype_of_sealed_class - -class _FakeResponse_0 extends _i1.SmartFake implements _i2.Response { - _FakeResponse_0( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeStreamedResponse_1 extends _i1.SmartFake implements _i2.StreamedResponse { - _FakeStreamedResponse_1( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeCocoonResponse_2 extends _i1.SmartFake implements _i3.CocoonResponse { - _FakeCocoonResponse_2( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeCocoonService_3 extends _i1.SmartFake implements _i3.CocoonService { - _FakeCocoonService_3( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeGoogleSignInService_4 extends _i1.SmartFake implements _i4.GoogleSignInService { - _FakeGoogleSignInService_4( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -class _FakeBrook_5 extends _i1.SmartFake implements _i5.Brook { - _FakeBrook_5( - Object parent, - Invocation parentInvocation, - ) : super( - parent, - parentInvocation, - ); -} - -/// A class which mocks [Client]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockClient extends _i1.Mock implements _i2.Client { - MockClient() { - _i1.throwOnMissingStub(this); - } - - @override - _i6.Future<_i2.Response> head( - Uri? url, { - Map? headers, - }) => - (super.noSuchMethod( - Invocation.method( - #head, - [url], - {#headers: headers}, - ), - returnValue: _i6.Future<_i2.Response>.value(_FakeResponse_0( - this, - Invocation.method( - #head, - [url], - {#headers: headers}, - ), - )), - ) as _i6.Future<_i2.Response>); - @override - _i6.Future<_i2.Response> get( - Uri? url, { - Map? headers, - }) => - (super.noSuchMethod( - Invocation.method( - #get, - [url], - {#headers: headers}, - ), - returnValue: _i6.Future<_i2.Response>.value(_FakeResponse_0( - this, - Invocation.method( - #get, - [url], - {#headers: headers}, - ), - )), - ) as _i6.Future<_i2.Response>); - @override - _i6.Future<_i2.Response> post( - Uri? url, { - Map? headers, - Object? body, - _i7.Encoding? encoding, - }) => - (super.noSuchMethod( - Invocation.method( - #post, - [url], - { - #headers: headers, - #body: body, - #encoding: encoding, - }, - ), - returnValue: _i6.Future<_i2.Response>.value(_FakeResponse_0( - this, - Invocation.method( - #post, - [url], - { - #headers: headers, - #body: body, - #encoding: encoding, - }, - ), - )), - ) as _i6.Future<_i2.Response>); - @override - _i6.Future<_i2.Response> put( - Uri? url, { - Map? headers, - Object? body, - _i7.Encoding? encoding, - }) => - (super.noSuchMethod( - Invocation.method( - #put, - [url], - { - #headers: headers, - #body: body, - #encoding: encoding, - }, - ), - returnValue: _i6.Future<_i2.Response>.value(_FakeResponse_0( - this, - Invocation.method( - #put, - [url], - { - #headers: headers, - #body: body, - #encoding: encoding, - }, - ), - )), - ) as _i6.Future<_i2.Response>); - @override - _i6.Future<_i2.Response> patch( - Uri? url, { - Map? headers, - Object? body, - _i7.Encoding? encoding, - }) => - (super.noSuchMethod( - Invocation.method( - #patch, - [url], - { - #headers: headers, - #body: body, - #encoding: encoding, - }, - ), - returnValue: _i6.Future<_i2.Response>.value(_FakeResponse_0( - this, - Invocation.method( - #patch, - [url], - { - #headers: headers, - #body: body, - #encoding: encoding, - }, - ), - )), - ) as _i6.Future<_i2.Response>); - @override - _i6.Future<_i2.Response> delete( - Uri? url, { - Map? headers, - Object? body, - _i7.Encoding? encoding, - }) => - (super.noSuchMethod( - Invocation.method( - #delete, - [url], - { - #headers: headers, - #body: body, - #encoding: encoding, - }, - ), - returnValue: _i6.Future<_i2.Response>.value(_FakeResponse_0( - this, - Invocation.method( - #delete, - [url], - { - #headers: headers, - #body: body, - #encoding: encoding, - }, - ), - )), - ) as _i6.Future<_i2.Response>); - @override - _i6.Future read( - Uri? url, { - Map? headers, - }) => - (super.noSuchMethod( - Invocation.method( - #read, - [url], - {#headers: headers}, - ), - returnValue: _i6.Future.value(''), - ) as _i6.Future); - @override - _i6.Future<_i8.Uint8List> readBytes( - Uri? url, { - Map? headers, - }) => - (super.noSuchMethod( - Invocation.method( - #readBytes, - [url], - {#headers: headers}, - ), - returnValue: _i6.Future<_i8.Uint8List>.value(_i8.Uint8List(0)), - ) as _i6.Future<_i8.Uint8List>); - @override - _i6.Future<_i2.StreamedResponse> send(_i2.BaseRequest? request) => (super.noSuchMethod( - Invocation.method( - #send, - [request], - ), - returnValue: _i6.Future<_i2.StreamedResponse>.value(_FakeStreamedResponse_1( - this, - Invocation.method( - #send, - [request], - ), - )), - ) as _i6.Future<_i2.StreamedResponse>); - @override - void close() => super.noSuchMethod( - Invocation.method( - #close, - [], - ), - returnValueForMissingStub: null, - ); -} - -/// A class which mocks [CocoonService]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockCocoonService extends _i1.Mock implements _i3.CocoonService { - MockCocoonService() { - _i1.throwOnMissingStub(this); - } - - @override - _i6.Future<_i3.CocoonResponse>> fetchCommitStatuses({ - _i9.CommitStatus? lastCommitStatus, - String? branch, - required String? repo, - }) => - (super.noSuchMethod( - Invocation.method( - #fetchCommitStatuses, - [], - { - #lastCommitStatus: lastCommitStatus, - #branch: branch, - #repo: repo, - }, - ), - returnValue: - _i6.Future<_i3.CocoonResponse>>.value(_FakeCocoonResponse_2>( - this, - Invocation.method( - #fetchCommitStatuses, - [], - { - #lastCommitStatus: lastCommitStatus, - #branch: branch, - #repo: repo, - }, - ), - )), - ) as _i6.Future<_i3.CocoonResponse>>); - @override - _i6.Future<_i3.CocoonResponse<_i10.BuildStatusResponse>> fetchTreeBuildStatus({ - String? branch, - required String? repo, - }) => - (super.noSuchMethod( - Invocation.method( - #fetchTreeBuildStatus, - [], - { - #branch: branch, - #repo: repo, - }, - ), - returnValue: _i6.Future<_i3.CocoonResponse<_i10.BuildStatusResponse>>.value( - _FakeCocoonResponse_2<_i10.BuildStatusResponse>( - this, - Invocation.method( - #fetchTreeBuildStatus, - [], - { - #branch: branch, - #repo: repo, - }, - ), - )), - ) as _i6.Future<_i3.CocoonResponse<_i10.BuildStatusResponse>>); - @override - _i6.Future<_i3.CocoonResponse>> fetchFlutterBranches() => (super.noSuchMethod( - Invocation.method( - #fetchFlutterBranches, - [], - ), - returnValue: _i6.Future<_i3.CocoonResponse>>.value(_FakeCocoonResponse_2>( - this, - Invocation.method( - #fetchFlutterBranches, - [], - ), - )), - ) as _i6.Future<_i3.CocoonResponse>>); - @override - _i6.Future<_i3.CocoonResponse>> fetchRepos() => (super.noSuchMethod( - Invocation.method( - #fetchRepos, - [], - ), - returnValue: _i6.Future<_i3.CocoonResponse>>.value(_FakeCocoonResponse_2>( - this, - Invocation.method( - #fetchRepos, - [], - ), - )), - ) as _i6.Future<_i3.CocoonResponse>>); - @override - _i6.Future<_i3.CocoonResponse> rerunTask( - _i12.Task? task, - String? idToken, - String? repo, - ) => - (super.noSuchMethod( - Invocation.method( - #rerunTask, - [ - task, - idToken, - repo, - ], - ), - returnValue: _i6.Future<_i3.CocoonResponse>.value(_FakeCocoonResponse_2( - this, - Invocation.method( - #rerunTask, - [ - task, - idToken, - repo, - ], - ), - )), - ) as _i6.Future<_i3.CocoonResponse>); - @override - _i6.Future vacuumGitHubCommits(String? idToken) => (super.noSuchMethod( - Invocation.method( - #vacuumGitHubCommits, - [idToken], - ), - returnValue: _i6.Future.value(false), - ) as _i6.Future); -} - -/// A class which mocks [BuildState]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockBuildState extends _i1.Mock implements _i13.BuildState { - MockBuildState() { - _i1.throwOnMissingStub(this); - } - - @override - _i3.CocoonService get cocoonService => (super.noSuchMethod( - Invocation.getter(#cocoonService), - returnValue: _FakeCocoonService_3( - this, - Invocation.getter(#cocoonService), - ), - ) as _i3.CocoonService); - @override - _i4.GoogleSignInService get authService => (super.noSuchMethod( - Invocation.getter(#authService), - returnValue: _FakeGoogleSignInService_4( - this, - Invocation.getter(#authService), - ), - ) as _i4.GoogleSignInService); - @override - set authService(_i4.GoogleSignInService? _authService) => super.noSuchMethod( - Invocation.setter( - #authService, - _authService, - ), - returnValueForMissingStub: null, - ); - @override - set refreshTimer(_i6.Timer? _refreshTimer) => super.noSuchMethod( - Invocation.setter( - #refreshTimer, - _refreshTimer, - ), - returnValueForMissingStub: null, - ); - @override - List<_i11.Branch> get branches => (super.noSuchMethod( - Invocation.getter(#branches), - returnValue: <_i11.Branch>[], - ) as List<_i11.Branch>); - @override - String get currentBranch => (super.noSuchMethod( - Invocation.getter(#currentBranch), - returnValue: '', - ) as String); - @override - String get currentRepo => (super.noSuchMethod( - Invocation.getter(#currentRepo), - returnValue: '', - ) as String); - @override - List get repos => (super.noSuchMethod( - Invocation.getter(#repos), - returnValue: [], - ) as List); - @override - List<_i9.CommitStatus> get statuses => (super.noSuchMethod( - Invocation.getter(#statuses), - returnValue: <_i9.CommitStatus>[], - ) as List<_i9.CommitStatus>); - @override - List get failingTasks => (super.noSuchMethod( - Invocation.getter(#failingTasks), - returnValue: [], - ) as List); - @override - bool get moreStatusesExist => (super.noSuchMethod( - Invocation.getter(#moreStatusesExist), - returnValue: false, - ) as bool); - @override - _i5.Brook get errors => (super.noSuchMethod( - Invocation.getter(#errors), - returnValue: _FakeBrook_5( - this, - Invocation.getter(#errors), - ), - ) as _i5.Brook); - @override - bool get hasListeners => (super.noSuchMethod( - Invocation.getter(#hasListeners), - returnValue: false, - ) as bool); - @override - void addListener(_i14.VoidCallback? listener) => super.noSuchMethod( - Invocation.method( - #addListener, - [listener], - ), - returnValueForMissingStub: null, - ); - @override - void removeListener(_i14.VoidCallback? listener) => super.noSuchMethod( - Invocation.method( - #removeListener, - [listener], - ), - returnValueForMissingStub: null, - ); - @override - void updateCurrentRepoBranch( - String? repo, - String? branch, - ) => - super.noSuchMethod( - Invocation.method( - #updateCurrentRepoBranch, - [ - repo, - branch, - ], - ), - returnValueForMissingStub: null, - ); - @override - _i6.Future? fetchMoreCommitStatuses() => (super.noSuchMethod( - Invocation.method( - #fetchMoreCommitStatuses, - [], - ), - returnValueForMissingStub: _i6.Future.value(), - ) as _i6.Future?); - @override - _i6.Future refreshGitHubCommits() => (super.noSuchMethod( - Invocation.method( - #refreshGitHubCommits, - [], - ), - returnValue: _i6.Future.value(false), - ) as _i6.Future); - @override - _i6.Future rerunTask(_i12.Task? task) => (super.noSuchMethod( - Invocation.method( - #rerunTask, - [task], - ), - returnValue: _i6.Future.value(false), - ) as _i6.Future); - @override - void dispose() => super.noSuchMethod( - Invocation.method( - #dispose, - [], - ), - returnValueForMissingStub: null, - ); - @override - void notifyListeners() => super.noSuchMethod( - Invocation.method( - #notifyListeners, - [], - ), - returnValueForMissingStub: null, - ); -} - -/// A class which mocks [GoogleSignIn]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockGoogleSignIn extends _i1.Mock implements _i15.GoogleSignIn { - MockGoogleSignIn() { - _i1.throwOnMissingStub(this); - } - - @override - _i16.SignInOption get signInOption => (super.noSuchMethod( - Invocation.getter(#signInOption), - returnValue: _i16.SignInOption.standard, - ) as _i16.SignInOption); - @override - List get scopes => (super.noSuchMethod( - Invocation.getter(#scopes), - returnValue: [], - ) as List); - @override - bool get forceCodeForRefreshToken => (super.noSuchMethod( - Invocation.getter(#forceCodeForRefreshToken), - returnValue: false, - ) as bool); - @override - _i6.Stream<_i15.GoogleSignInAccount?> get onCurrentUserChanged => (super.noSuchMethod( - Invocation.getter(#onCurrentUserChanged), - returnValue: _i6.Stream<_i15.GoogleSignInAccount?>.empty(), - ) as _i6.Stream<_i15.GoogleSignInAccount?>); - @override - _i6.Future<_i15.GoogleSignInAccount?> signInSilently({ - bool? suppressErrors = true, - bool? reAuthenticate = false, - }) => - (super.noSuchMethod( - Invocation.method( - #signInSilently, - [], - { - #suppressErrors: suppressErrors, - #reAuthenticate: reAuthenticate, - }, - ), - returnValue: _i6.Future<_i15.GoogleSignInAccount?>.value(), - ) as _i6.Future<_i15.GoogleSignInAccount?>); - @override - _i6.Future isSignedIn() => (super.noSuchMethod( - Invocation.method( - #isSignedIn, - [], - ), - returnValue: _i6.Future.value(false), - ) as _i6.Future); - @override - _i6.Future<_i15.GoogleSignInAccount?> signIn() => (super.noSuchMethod( - Invocation.method( - #signIn, - [], - ), - returnValue: _i6.Future<_i15.GoogleSignInAccount?>.value(), - ) as _i6.Future<_i15.GoogleSignInAccount?>); - @override - _i6.Future<_i15.GoogleSignInAccount?> signOut() => (super.noSuchMethod( - Invocation.method( - #signOut, - [], - ), - returnValue: _i6.Future<_i15.GoogleSignInAccount?>.value(), - ) as _i6.Future<_i15.GoogleSignInAccount?>); - @override - _i6.Future<_i15.GoogleSignInAccount?> disconnect() => (super.noSuchMethod( - Invocation.method( - #disconnect, - [], - ), - returnValue: _i6.Future<_i15.GoogleSignInAccount?>.value(), - ) as _i6.Future<_i15.GoogleSignInAccount?>); - @override - _i6.Future requestScopes(List? scopes) => (super.noSuchMethod( - Invocation.method( - #requestScopes, - [scopes], - ), - returnValue: _i6.Future.value(false), - ) as _i6.Future); - @override - _i6.Future canAccessScopes( - List? scopes, { - String? accessToken, - }) => - (super.noSuchMethod( - Invocation.method( - #canAccessScopes, - [scopes], - {#accessToken: accessToken}, - ), - returnValue: _i6.Future.value(false), - ) as _i6.Future); -} - -/// A class which mocks [GoogleSignInService]. -/// -/// See the documentation for Mockito's code generation for more information. -class MockGoogleSignInService extends _i1.Mock implements _i4.GoogleSignInService { - MockGoogleSignInService() { - _i1.throwOnMissingStub(this); - } - - @override - set user(_i15.GoogleSignInAccount? _user) => super.noSuchMethod( - Invocation.setter( - #user, - _user, - ), - returnValueForMissingStub: null, - ); - @override - bool get isAuthenticated => (super.noSuchMethod( - Invocation.getter(#isAuthenticated), - returnValue: false, - ) as bool); - @override - _i6.Future get idToken => (super.noSuchMethod( - Invocation.getter(#idToken), - returnValue: _i6.Future.value(''), - ) as _i6.Future); - @override - bool get hasListeners => (super.noSuchMethod( - Invocation.getter(#hasListeners), - returnValue: false, - ) as bool); - @override - _i6.Future signIn() => (super.noSuchMethod( - Invocation.method( - #signIn, - [], - ), - returnValue: _i6.Future.value(), - returnValueForMissingStub: _i6.Future.value(), - ) as _i6.Future); - @override - _i6.Future signOut() => (super.noSuchMethod( - Invocation.method( - #signOut, - [], - ), - returnValue: _i6.Future.value(), - returnValueForMissingStub: _i6.Future.value(), - ) as _i6.Future); - @override - _i6.Future clearUser() => (super.noSuchMethod( - Invocation.method( - #clearUser, - [], - ), - returnValue: _i6.Future.value(), - returnValueForMissingStub: _i6.Future.value(), - ) as _i6.Future); - @override - void addListener(_i14.VoidCallback? listener) => super.noSuchMethod( - Invocation.method( - #addListener, - [listener], - ), - returnValueForMissingStub: null, - ); - @override - void removeListener(_i14.VoidCallback? listener) => super.noSuchMethod( - Invocation.method( - #removeListener, - [listener], - ), - returnValueForMissingStub: null, - ); - @override - void dispose() => super.noSuchMethod( - Invocation.method( - #dispose, - [], - ), - returnValueForMissingStub: null, - ); - @override - void notifyListeners() => super.noSuchMethod( - Invocation.method( - #notifyListeners, - [], - ), - returnValueForMissingStub: null, - ); -} diff --git a/dashboard/test/utils/output.dart b/dashboard/test/utils/output.dart deleted file mode 100644 index 4aa7d191b..000000000 --- a/dashboard/test/utils/output.dart +++ /dev/null @@ -1,37 +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:flutter/foundation.dart'; -import 'package:flutter_test/flutter_test.dart'; - -typedef AsyncVoidCallback = Future Function(); - -/// Checks that any [debugPrint] output generated by `block` matches -/// the output given by `output`. -/// -/// Calls to [debugPrint] inside `block` will not generate output to the -/// console, but if it doesn't match `output` then it will be presented -/// as part of the error message as the "actual" result. -/// -/// Output to [debugPrint] that contains newlines is split into separate -/// entries when comparing to `output`. -/// -/// This method must be `await`ed. -Future checkOutput({ - required AsyncVoidCallback block, - List output = const [], -}) => - TestAsyncUtils.guard(() async { - final DebugPrintCallback originalDebugPrint = debugPrint; - final List log = []; - debugPrint = (String? message, {int? wrapWidth}) { - log.addAll(message!.split('\n')); - }; - try { - await block(); - } finally { - debugPrint = originalDebugPrint; - } - expect(log, output); - }); diff --git a/dashboard/test/utils/task_icons.dart b/dashboard/test/utils/task_icons.dart deleted file mode 100644 index 75a24adfb..000000000 --- a/dashboard/test/utils/task_icons.dart +++ /dev/null @@ -1,35 +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 'package:flutter/widgets.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:path/path.dart' as path; - -/// Precaches the images in the `assets` folder. This method must be called -/// before pumping any widgets. -Future precacheTaskIcons(WidgetTester tester) async { - // Depending on how we're invoked, Platform.script.path will have extra parts - // after app_flutter. Just trim them off. - final List pathParts = path.split(Platform.script.path); - while (pathParts.last != 'dashboard') { - pathParts.removeLast(); - } - - final String assetPath = path.joinAll([...pathParts, 'assets']); - final List assets = - Directory(assetPath).listSync().map((FileSystemEntity entity) => path.basename(entity.path)).toList(); - await tester.pumpWidget(const SizedBox()); - await tester.runAsync(() async { - for (final String asset in assets) { - final ImageProvider provider = ExactAssetImage('assets/$asset'); - await provider.evict(); - await precacheImage( - provider, - tester.allElements.first, - ); - } - }); -} diff --git a/dashboard/test/utils/wrapper.dart b/dashboard/test/utils/wrapper.dart deleted file mode 100644 index 3a6d4cc83..000000000 --- a/dashboard/test/utils/wrapper.dart +++ /dev/null @@ -1,43 +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:flutter/material.dart'; -import 'package:flutter_dashboard/widgets/now.dart'; -import 'package:flutter_dashboard/widgets/state_provider.dart'; -import 'package:mockito/mockito.dart'; - -import 'fake_build.dart'; -import 'fake_google_account.dart'; -import 'fake_index_state.dart'; -import 'mocks.dart'; - -class FakeInserter extends StatelessWidget { - const FakeInserter({super.key, this.child, this.signedIn = true}); - - final Widget? child; - - final bool signedIn; - - @override - Widget build(BuildContext context) { - final MockGoogleSignInService fakeAuthService = MockGoogleSignInService(); - if (signedIn) { - when(fakeAuthService.isAuthenticated).thenReturn(true); - when(fakeAuthService.user).thenReturn(FakeGoogleSignInAccount()); - } else { - when(fakeAuthService.isAuthenticated).thenReturn(false); - when(fakeAuthService.user).thenReturn(null); - } - - return StateProvider( - signInService: fakeAuthService, - indexState: FakeIndexState(authService: fakeAuthService), - buildState: FakeBuildState(authService: fakeAuthService), - child: Now.fixed( - dateTime: DateTime.utc(2000), - child: child!, - ), - ); - } -} diff --git a/dashboard/test/widgets/accessibility_test.dart b/dashboard/test/widgets/accessibility_test.dart deleted file mode 100644 index 642c4c1c7..000000000 --- a/dashboard/test/widgets/accessibility_test.dart +++ /dev/null @@ -1,84 +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:flutter/material.dart'; -import 'package:flutter_dashboard/model/commit.pb.dart'; -import 'package:flutter_dashboard/widgets/commit_author_avatar.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() { - group('Author avatars meet guidelines for theme brightness', () { - List generateInitials() { - final List names = []; - - for (int i = 65; i <= 90; i++) { - names.add(String.fromCharCode(i)); - } - for (int i = 0; i <= 9; i++) { - names.add(i.toString()); - } - return names; - } - - const List longNames = ['Michael', 'Thomas', 'Peter', 'Volkert']; - - Widget buildAuthors({required List names, ThemeData? theme}) { - final List avatars = names - .map( - (String name) => CommitAuthorAvatar( - commit: Commit()..author = name, - ), - ) - .toList(); - - return MaterialApp( - theme: theme ?? ThemeData.light(), - home: Scaffold( - body: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Wrap(children: avatars), - ], - ), - ), - ); - } - - testWidgets('dark theme', (WidgetTester tester) async { - await tester.pumpWidget( - buildAuthors( - theme: ThemeData.dark(), - names: generateInitials(), - ), - ); - await expectLater(tester, meetsGuideline(textContrastGuideline)); - }); - - testWidgets('light theme', (WidgetTester tester) async { - await tester.pumpWidget( - buildAuthors( - names: generateInitials(), - ), - ); - await expectLater(tester, meetsGuideline(textContrastGuideline)); - }); - - testWidgets('long names, dark theme', (WidgetTester tester) async { - await tester.pumpWidget( - buildAuthors( - theme: ThemeData.dark(), - names: longNames, - ), - ); - await expectLater(tester, meetsGuideline(textContrastGuideline)); - }); - - testWidgets('long names, light theme', (WidgetTester tester) async { - await tester.pumpWidget( - buildAuthors(names: longNames), - ); - await expectLater(tester, meetsGuideline(textContrastGuideline)); - }); - }); -} diff --git a/dashboard/test/widgets/commit_author_avatar_test.dart b/dashboard/test/widgets/commit_author_avatar_test.dart deleted file mode 100644 index 07ba0d66e..000000000 --- a/dashboard/test/widgets/commit_author_avatar_test.dart +++ /dev/null @@ -1,35 +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:flutter/material.dart'; -import 'package:flutter_dashboard/model/commit.pb.dart'; -import 'package:flutter_dashboard/widgets/commit_author_avatar.dart'; -import 'package:flutter_test/flutter_test.dart'; - -void main() { - testWidgets('Authors with same initial have differently coloured avatars', (WidgetTester tester) async { - final Commit commit1 = Commit()..author = 'Mike'; - final Commit commit2 = Commit()..author = 'Michael'; - - await tester.pumpWidget( - MaterialApp( - home: Column( - children: [ - CommitAuthorAvatar( - commit: commit1, - ), - CommitAuthorAvatar( - commit: commit2, - ), - ], - ), - ), - ); - - expect(find.text('M'), findsNWidgets(2)); - final List avatars = tester.widgetList(find.byType(CircleAvatar)).toList(); - expect(avatars, hasLength(2)); - expect(avatars.first.backgroundColor, isNot(avatars.last.backgroundColor)); - }); -} diff --git a/dashboard/test/widgets/commit_box_test.dart b/dashboard/test/widgets/commit_box_test.dart deleted file mode 100644 index 77fcc505f..000000000 --- a/dashboard/test/widgets/commit_box_test.dart +++ /dev/null @@ -1,128 +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:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_app_icons/flutter_app_icons_platform_interface.dart'; -import 'package:flutter_dashboard/model/commit.pb.dart'; -import 'package:flutter_dashboard/widgets/commit_box.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; - -import '../utils/fake_flutter_app_icons.dart'; -import '../utils/fake_url_launcher.dart'; -import '../utils/golden.dart'; - -void main() { - final Commit expectedCommit = Commit() - ..author = 'AuthoryMcAuthor Face' - ..authorAvatarUrl = 'https://avatars2.githubusercontent.com/u/2148558?v=4' - ..message = 'commit message\n\nreview comments' - ..repository = 'flutter/cocoon' - ..sha = 'ShaShankRedemption'; - final String shortSha = expectedCommit.sha.substring(0, 7); - final Widget basicApp = MaterialApp( - theme: ThemeData(useMaterial3: false), - home: Material( - child: Center( - child: SizedBox( - height: 100.0, - width: 100.0, - child: CommitBox( - commit: expectedCommit, - ), - ), - ), - ), - ); - - setUp(() { - FlutterAppIconsPlatform.instance = FakeFlutterAppIcons(); - }); - - testWidgets('CommitBox shows information correctly', (WidgetTester tester) async { - await tester.pumpWidget(basicApp); - await expectGoldenMatches(find.byType(Overlay), 'commit_box_test.idle.png'); - }); - - testWidgets('CommitBox shows overlay on click', (WidgetTester tester) async { - await tester.pumpWidget(basicApp); - - expect(find.text(shortSha), findsNothing); - expect(find.text(expectedCommit.author), findsNothing); - - await tester.tap(find.byType(CommitBox)); - await tester.pump(); - - expect(find.text(shortSha), findsOneWidget); - expect(find.text(expectedCommit.author), findsOneWidget); - - await expectGoldenMatches(find.byType(Overlay), 'commit_box_test.open.png'); - }); - - testWidgets('CommitBox overlay shows first line of commit message', (WidgetTester tester) async { - await tester.pumpWidget(basicApp); - await tester.tap(find.byType(CommitBox)); - await tester.pump(); - - expect(find.text(expectedCommit.message), findsNothing); - expect(find.text('commit message'), findsOneWidget); - }); - - testWidgets('CommitBox closes overlay on click out', (WidgetTester tester) async { - await tester.pumpWidget(basicApp); - - // Open the overlay - await tester.tap(find.byType(CommitBox)); - await tester.pump(); - expect(find.text(shortSha), findsOneWidget); - - // Since the overlay positions itself a little below the center of the widget, - // it is safe to click the center of the widget to close it again. - await tester.tap(find.byType(CommitBox)); - await tester.pump(); - - expect(find.text(shortSha), findsNothing); - }); - - testWidgets('tapping sha in CommitBox redirects to GitHub', (WidgetTester tester) async { - final FakeUrlLauncher urlLauncher = FakeUrlLauncher(); - UrlLauncherPlatform.instance = urlLauncher; - - await tester.pumpWidget(basicApp); - - // Open the overlay - await tester.tap(find.byType(CommitBox)); - await tester.pump(); - - // Tap the redirect button - await tester.tap(find.byType(Hyperlink)); - await tester.pump(); - - expect(urlLauncher.launches, isNotEmpty); - expect(urlLauncher.launches.single, 'https://github.com/${expectedCommit.repository}/commit/${expectedCommit.sha}'); - }); - - testWidgets('clicking copy icon in CommitBox adds sha to clipboard', (WidgetTester tester) async { - final List log = []; - tester.binding.defaultBinaryMessenger.setMockMethodCallHandler( - SystemChannels.platform, - (MethodCall methodCall) async => log.add(methodCall), - ); - - await tester.pumpWidget(basicApp); - - // Open the overlay - await tester.tap(find.byType(CommitBox)); - await tester.pump(); - - // Tap the copy button - await tester.tap(find.byIcon(Icons.copy)); - await tester.pump(); - - expect((log.last.arguments as Object) as Map, { - 'text': expectedCommit.sha, - }); - }); -} diff --git a/dashboard/test/widgets/goldens/commit_box_test.idle.png b/dashboard/test/widgets/goldens/commit_box_test.idle.png deleted file mode 100644 index ede67a96156027882d5ed528d61036143383f8fb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28083 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 diff --git a/dashboard/test/widgets/goldens/commit_box_test.open.png b/dashboard/test/widgets/goldens/commit_box_test.open.png deleted file mode 100644 index cb4e720e850b6cbef92b46459aefa32b88fb2e5f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 31373 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 To|)|eJo6=4MQrkUy}$k!5*TE- diff --git a/dashboard/test/widgets/goldens/sign_in_button.authenticated.png b/dashboard/test/widgets/goldens/sign_in_button.authenticated.png deleted file mode 100644 index fb91c430fb4af2730a9b30d481c2f0d622d4ec99..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22873 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 diff --git a/dashboard/test/widgets/goldens/task_grid_test.dev.mouse_scroll_y.png b/dashboard/test/widgets/goldens/task_grid_test.dev.mouse_scroll_y.png deleted file mode 100644 index 9eb3ef1fac417e196d9beb15baa2f552ca36d0e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 35674 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 diff --git a/dashboard/test/widgets/goldens/task_grid_test.dev.origin.dark.png b/dashboard/test/widgets/goldens/task_grid_test.dev.origin.dark.png deleted file mode 100644 index c9873d9cdbb38bf7625f4d5af1d18b38bdf12e75..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34196 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�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 Ol2W;>1}{=H4f;RNUj%yq diff --git a/dashboard/test/widgets/goldens/task_grid_test.dev.scroll_x.dark.png b/dashboard/test/widgets/goldens/task_grid_test.dev.scroll_x.dark.png deleted file mode 100644 index f51f434dc993d8b7aa0e513e9ce8ced3fba40ced..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23910 zcmb?@1yogA*Y-vP6fa^RB7G&K5hSFsND-tPqy+@&h9iiIl(aMmNP~cYG$@^dbRDF- zyUxG%0ln9IeZTkr{_*c|#(;g!jMYyivH@WM5-&b9ZMBVJU^QC1I&90%U{ z?4?azrQ*AMBU$2x-`BJK!s5i~V?sum!seFWHg97NU@dwEnA9U^_~ern??+HQeD|rx z;Y^P5)rIB+OPd@wW2Y=QA zmze-QzS|ocK2=!TNEqHd`96x|4eD{Xcn2!+pC>KN(N7&6>=loJ0S-P#M0^fD>DmAH zU74bJ7o~2C;Y)KA|7iK_N*uX}FnrgOFMeq`{MG;6rgx)>;zM`DmX?r>hR73qA=isE0t|r9nr(CO$%@&na6z~^1{6tJb62{MU{d%0ApC9K$Z<&)_x9+!Z--;d946?pR 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

J9@wr+ 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 diff --git a/dashboard/test/widgets/goldens/task_grid_test.dev.scroll_x.png b/dashboard/test/widgets/goldens/task_grid_test.dev.scroll_x.png deleted file mode 100644 index 171433ccb3900c1e3e08948ef5f16ccd1aa204c4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24573 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 diff --git a/dashboard/test/widgets/goldens/task_grid_test.dev.scroll_y.dark.png b/dashboard/test/widgets/goldens/task_grid_test.dev.scroll_y.dark.png deleted file mode 100644 index efeaa8a96957199fea4b05462b26d2e27d7ec42e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 34043 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

No|Z1DU?>yom? zCK>EC-;{7gzaf>5o`H+{}(E)&N!*aXFlfiFg6hC zV`6oj4LErGhr1=?c?jfg(VKEA04-s4^u&%zCcRH>Z8$JmtJ8HdgCY#fwK<+ywm0+g z4?6SAKl;DuC*M@dh9e%@pS}IKAsXsZhvx2LwO^zXj=eD#(j`FM+@PuZ7=K`Cs^2B4 zmzCdar%C~*Rq-0~K1mr7Qho?~{b6Z0C{V-pJ{MGJ&v1lFdO4`1Cpmt1+V407X4Y_K z==e_ifEXSGh~eoPx%)#1Y3)DJEAANOu~GV%&p@;RF2a%oV*~G{#_oKfCTBQ90g+W ziyQw?$(ykKEa0oa1|3eo zFuRc(s11a#jOkMQMPM+w@F#w{984Dwo?#Gkhin+|FAOfED@f)?wL}foe zzi|*lx*(b{1jX@rQ*(em3`R+PnEG*W2bPVz{_niDpl~`a;Zht&$yckNHDm>bW9SY{ z)U+Y{m~YnQG)K6CH8n@sYqm~uuG=FO%=7Ss^$2a`!bTh@0vu-4gsbm<;*@@l+}$mDKcuC;D*Vc{>=0(aVj6TpRSo|SolwR#re)~d z0NKef+<8#OZoIL3?)9fFEnp0cf?+laSJ+uzrH9s`a%kR4IFHYM;|Y!u3&JRPHR->oIa)Gx;x4Q@E} zVg>EKk;8b>Fj2=C7K8fo%R3ut7)E{kjpAd+w;A96x{PnGD?JmJ1>KyjCn7+c;G}i e1NaB#xvI1nq^O^49sCFWxtOdP68Lx_nT0?gR1jH-M`~{GHfKB?@Wvb1&Ucc7 zrQ(p2g1J0w!aE;zm3)b@rIleuQu9g;k8KuaoG!UqL9tw7m<26y&!m!H+_2l|_FAmHUEf~E zo>O|0H|ShEcdlOLQS0k`q65MHn-6hnhzl&W8&31hH^yT&m`$V-^aJFmGx$y~e^cQv z9O*xjlpJ~)_*QVqZhw73>vv;0Ohrrs{bc%S`C&oJjkWvp3-gd76)Y;mi{#l96U3+3 zgcWj7L;lCz|57Mq=U)o=v!wqYzT7spDcS|L%IM!b>mRL!xZMc|fqtA@VgKpN?XUkY zeq4s?io~d*D7{z~fjmAxUaf4&q50W$wOy$za?WfgYngy0hNvU5(`@RKUVJsnwyQje ziRi8I&okiB<#XKO4IWkR& ze$G66P`A^yGj!8*Ez&d~eYqE(QH6Ox+`D#W6tbZ{Qb_5MZ}>J)Hw-h^69IU4%4EFTl?R$$ELH0qiLqfdPz2T>(pJ>;ObTKWhS2UCUNo+*0F~PIZ z=bzu&o;Gct4Q*+1Rlm8f*8E|QK{>HteBE>~8ZD34 z*0-VF1S=ueJ9Zx#IitFGhpumsIr@|7&Ny?W0fqY#cAv>Ws(@zM1h}7zYvl13Yfl^8 z+z2y|3Keg$=GBfLgvo=BI$*zXJE}>yOmMr>l2Dh8iD`0+hs6{Z8(Re{tJ8u&Ct~I9 zSaxyBc)SXWDAk)2Zk<*dYJLvcvI&opsjcL83K5_BXC(L^`9G#Fh{bYn{Ko(JGGnH zfayn)y!msG-6aQ<30uQY>-^sMwGPbMes5%aH)@Qpi*HnRKnCTT?w%f00nF7G<`cG& znI$FI#KgqntkGr{@FXJdKuzTgY~F~mf`?hEvG3-Vmp^`H!d{ih|Pa&(=>2!a1fhL`JLU#yy%`{>*pv+5ef%^+!AjL@`rdTo}gtY z(Sojsr`Kdn1EYLR7sA5CDxR6j-q&ZV5Lh99y4^H8ZJE8wVf6}Toc{85ADGOivCYt# z#rUwC8E46ofGWHA)g4S&BxSz|6gf;7zASH)Opc<@hj%|%3ls}D9$RJo)PCp0{l4%d z@2BI@iUXg+>8|nU;@G!u?>~iBS%4nSd}LrZoQ<^St%bR6r6R{+DM9_S@T zXSk^!{NG;29nPL1tZ@z*6PR%nTzVV{-zFE>~9ssH%l~cZK!P-h*K6?A~FAkYPsC0x?=W zzZZp2&IG7L#Z zx-k6ursagkkIDG>;1d>HdnKK<8hJ-vLlqnwPkq2ehZo^Uhoa=l+SktvlN@6_h>*ec zZ82-5`@(g{n3siDf~+n+na@tT1a_0uxZigd>DBoA`@{CW<-BvS(kJfX!b7i+98g}) z1re2(M`hKo4G0QCBolB!J34CJiRA*@;k=ZAt+;PHC)N}5X09tk>g@(h8ai~7&2e0w zo_?1u0*35G8ZYx36nEj{9Mbpm%asU7khZJrr3gVC$EGSy_rL%z4AJD4(>}3~zOf-& zf%eO=>)jEwZIe}oJVL@&S#NKla8yu_u~eE!I1B+H-YYzODu_dt-)ZD@x<*#1OIFXG%zv$T-3{jYRq{ ztVL2n_j4MWcK^lz#oKFt`@@x>6@!6vOZGZK_*>CXW%STK*f`$hu5d0y=b$xsCk+_E zM4ebDJ+NB)#}*Uea}C>sd57bCPN`t2Rpm2?6|lzfQV+m6dDNBDyI-uxB~jwHB*KL} z!o`Ly)oykk4t`ndB7d1~UWC(XM8foJEN;HtG<2J?TvxdhtNGxPq_f z(FeHd6^&I)!+Zg}A)lAfr;d=1pnw}0US?<0LThV3V{0-OY!g~EKEEbwUi{L0ac6!? z6XE;lCs|>aI`WP2;?C|j_pvD{cd7*M-$&Kf*48Swz&<}e|7_ZcVe`Edfi+S7wO7|E zh?48G;+a64iEyN_XnWas*il29=WOcK1kdP2LK0VP+gp%PVF@U!N|Ot^Qcf8S(yH*m z&cUrp1qO2XjpZ1o(aae^Z+JNP8=r**BcB4Ku~d6+{g_;v`If+Hhk4lU?r!M)VbRf@ zKS%Q&=UWh#R#)@WFx!KuQDb`EY~Kk$R{t`FoFjrg8Z6}aaRMcoao<=a`3Jr%P zV_Y&c0lpx;21D{SDs_Ya3I`agA>IJLGxPgK?%Q-!dqsm&46_LGXW{H@p0HT4hBrvm z$_55eKYv<9knulQI=(9%)pmV#QDWSRh=jkNF@dv-&0M3D++=Z3kFeL58oj6Vo#tn* zWy-~h2yTE{6t$DIcFpkJZ``n%yx>V z&iw1^OEhWd$ri-JM-G_f^k$$vD{OFlzEJc zeAY7R*2Qr-C@p=U#Z0Y4$4i?(8WtCaGrADFcXo9|d+d%%ClPn7-(!~Yy$-(Fj?Q|{ zmNd}jqL(^xd~A${lXI8x3tRmzY+ha-ENf5YK%2#sg>y|^eD)(~uaRP zeH3l!!rGQp5|c_!y=+=F6`Pot;~@;XPDVjNQeIwO=1kdUzYdP(!@C^KRh-7}a^tCQ z;)OKzn6~#)n<=O=8Ct9BK%=RJ%^STM0c|Bq}xEy)p8(CJ45Zzd@?} z4}%XID<6fkwJCHu=)tq1B%TFhS`!GoGj9Pd?BsMlSLsqy-QCX%n(GZ4)Y5>+Gfg%+( zssEn-iC3rO(nQ&n@W8-8wA(}_rzU>RuTgT}M#C~HzsFtV>qVK4R@1k)*a>%>FC`p| zK^~SX)Q-_4{rPdTr^kzJTsPhIS{(vp^U2#2UhH1L6Iw<(rCw~V6x#$LU)w-kD`SrqVF7M_(=me+HJ?Mvnc@xF-4hqfa`=XfV$ULT8kFiFJ<^)FD5 z;!mgtJq}CC8Q6*4^xS;XslP7NrJ9;*vD2ZLWUqi2Fn#H=xf?(cQp5w;D9dZhd%`Lf?t{m z^orHU=ZVxi%=QFo2AX%LYSY?Cf*DS} z!t1U3o^rOUG|2mPN^@V>lx|Rjt{SMTw$^vr!_nC?&g1NMCIL3ZmbCfNr@uCL$UJzU zz82}(CcB**@%`FF=toT;OWY`I#L7#go)Cp;Z|qLn@A6F&remS1jH6N zAVT*JM^Tvwm9%UKvu~nE=_57xtv)=Q0ECxfPCq$;?JzEBJg#Z3Cl-mBe^{bR; zoJbaCg>~~5?v^a^on_PVt>;(L{3?CsZInvH9vAHCU;ZaG1n80{$(Yh#pi_#5W+p%o zGeodqX43qqpBq*1CoYY&3|exCG749U7azZol)hK`@_@-oo-r>r^=(rEr5#~tqBXSY z7vnTN%zSH*X%t|wu6fnzkwaxwsm7ODb(Hjc%U1Q455}S@^-X*vo03DN-x^d^pUB}FAbcT#i z8|J<7*KlRbyb|MuwltO=0H|%d%NNwN$FSJ=l)QAXnUhylw({yBfp5QJE-bt2mN>Nd z`?Yr#7SP|{rxy+bkV-O2NH-G74S*w^gg61!c9Xg=#5fP7Qc>$lwtk!cqWh!;or>zS zvmAC>pATYa!r(@xggornfL?Is*FY?+Jtk>KgGg)C_drA{BTW3NsSt%nK380UF%|!SavgcEE zB`f2Y9(1L61pNvr4jQ(YocP3;V(@USEIFB^K(|U!#WT2-W9=l)QbbzX{tn61KwwICy@(CIKn~CBqCz9M~dYzs?#Q;6Uo$6!-#wT)M>>tY@IB8{XU7oCV*s+k%># zmxof#JSB{qWt%m$ysa81#Yg}-bq;wt0OO{&g#p+CfRg)fFp!o68y=p(l)WhuxKAhj zI~9i_eJ4jGM#5a zv&!Y=d!RIugv25G4Zm=}ex@iw*oEqY??%Q4bpKN%gf5Xf5oy(y!7RAd<>kQC;DcJu&B{lY z2I22-D=g_PzNKt>hqIo)Zr1*=k+xGELgraHw|aJV)&(|f#%Sn%ozeD%ROI#s=Wx+z zqlO&a9bqUPqt1$h*l9Py$^&f^wl~;^ON5j?keZ3N>IlH(krz>|tvsm3H81ALOKWG+ zgq!pz&Z9`Y?ViiC0XUif_%?d-^CF~*FV=*v`43IrjIXvw9n`6XOW#)Z1%WJx*G}i_ zBMT2g39d;B#(yPC41@n1;Tx6DyGQz11A(^pmmrD|83^R0#@6cSJL=2MR@=)j%ULpV zM8O~4N@COPt<0S)RF?^765UpVmkf5ih`O6kWqxjA%hz5pjmwpmAO6FR=ksgF!yX#< z?+>u^e1AwTDvbDEILrl2sh-Op17&Bm-eh~V4kmO7WYfP^=PjymPQQwaK5xf`t z#*__aJo(`}Z#vCnFMGHooM$5GC?ldJwm(VKgG;P-wwl4#I^W1K*Ir)oB$nUf!DgsS zL`5VF#JRR^aB?JUpy&$D*IFbxJT*P{yAVXC0uBc{0O;mbETRx9JE^b4?&S+UI}=Oz zIQ#q%OO!=kRB9gGsVSGRGyJRh9OCKrex-6o)?ab@GzgeOfz}|wtz1bvo0e0ylDMI_u@A!}iL-0mU zy}kCgKem~J$Q5}2Rjrb`R9yK(sMU@XQVnGU?9#D)1-UIQh-`g1(!ao73Q2q7D2Urr zW|15IxF%^^>m)oNj<^*XI6Z`aoE%Weoe=(y-DA3OX3{m$wMctGB^{3+)(01xC&lkI z9L|e9?*x%>^;V}pc!5`d7oaHo?8e4WeAspE^jaxf7T(LpIu1fFOEpgWc?@@MZu3)~ z*X%i)ZJk|jwlMSB3(ual=$Fu5#t!^hi4and%kEAedH>jpn&(khZ_F$Sw#;i)yAwS{ zoeiHXC)4TuxGOQFQMOrJme4NQuN|B5WZBU&okUX>5K(hOh9W)t4(URMp&t(;)2=Bh z>Y~tHBmKZh%L+0Nuuel_aWhr{0$Yw_y~({xhh~@Nb<+!2i(OpC&kB|0T@9Fw0d8xMsM{P{ZuPwC60c(D$}d; zBdr=Ix`oBXs|0gEU;u6)oPb5l(2&w)bK)i7L7SJIB&l^5t^u6V!ak&Pb{#E7rT$D9 zl6zXsge|64cVeTPC&t|S*awSkU@9ySmjIqw*zPH}b}a9nO1+@{eW%UuR@p`tAGJmN zUaab}v$3LRd7Xxazd9?S(Q<7(dhtliwLB1&aG@m-l@V0##^ITNdg@kS&_F`Sswe8> z(}+PlT;=Es1w6#15XYTQUXWome4EtI=eTY&Wvg%gcl?0k>ECh#tqvE;%3?_IiHTXY zwItx6j8=2i6??bD_r}L&;xmlDuSoLZ?Q`QnDFXZS?<3>K1XR}-D09cBPq1Gs`Iz4B ztn;4Y+f%&ac!ZdLPZB)k`gj)I40zgD1Z>!q&h&syi{KrH)Q6!ekF_doh;O-F>Htwm zNrZQb49A<_vs9nqA)OEcDml{+A*8DKN>+3_$GGW9(>C0Hxf2H&zx^nJ)H6bg=uPnT zxTN43^RTMw%gH$6s1xmCvWTy+%l0(?TA9qX&%OKi=baxqE_Gp#I%V5CK)bqN-?Lt> z@p#hJD<-9UM@N`#ZklA66YdYPp!pB7Ae?7pAszDUs1lQzAZb4#moOTwu%G~qK)&k< z9Qs$Qip=b6i3&F^qT46A*Ho4`KB>v*M_VY6AX++ZZUAdk6?e1)fL5jVN=%QnwRP;< zx5N(LQQ7MK0Ab+Q19p6lNgfjYZB{x`qh1j;xLJMJ0jjc4n6Jg6-SH->yM^I^TJ8`dcuUym-%q z2gl9sd{Tr$hq9g?+3S)WH66PV$#4Qof|QUDB%o~|iB`c%K>ooU6cTb+Iz8^7H%$`u z_ITXU_{6eC!Mrc|gEkarQ0YZGC?TD1F)x zfYqLF@66brxqHuApWm2yh+p+A zhkc!Jy|>LqLlgG&C1R}oZ|Kq9|0ndgLmId5@@_oUxQZLaeVsF8DI?t?-0Y&ON5Q!2 z!^GgkL`XzL1a+eH{>k%=09QQzUu6NZ1ds1Nt+PAo5Ur!edhMzJD@|wG)``21k55~x zR9a0QA9`!tYCi-fs7KxO;`CNDdeFGo8GYwcKxJ`fbudF5yMn>@*JA=1=_t*vA*Fpv zPII(WhHr%60wE+p(qFI-)~tGDKl3$V`Ojaoaz)c`!bjsz6eDu~N@jiX4>Ez^Pcot5 zFGRvSeD~_-d}MrHr=Nd1T3cBSeruCH{ni0pgG+jSDX^$%1Au-s0Q*}%QkO2+|A%^H zbfM9lzYtyEO=?^b}h4pw*;8fvkh`C)Asr6Wa3JV4LuX6AGV6S%+ zxzCTJu;U*hHMKt+LwV$6;A(PlmYVle#eG}d`OuQrBYt*inf|cpGCi+-<3^+@;8rst zPHCMN)z*G%wWm?2gOI~{e8-LbUd4%~r6m(Ive#(_8MUy)WCk{-G-Nn`At}y#(?|klH^-UlgS5H+=McB$BW=nvCMpc!pzLSe^@J|17`OW}qJP>i47b7o9YuzBB6{R6?phuil29fAv0{}=Wl zWB${>&=1rvZdW^IjH7r4~E)tmgdAjhQnHMBqQRMXgH>+KzNMey=9 zKJl>@{J$k5JfQ@{jPTuSH><&84jo}grjYE?d#{M&lipi51H!2=*S9@{SAeXzYX8IE z5OM8oQNG2gCQOC^x&nNharXtOa;oI5@OIN%mivIv2cUfo-I!}jB%hvQWE9Cdf}NPzQJ(u<5F;YIFetQ77ad!9}p{R>%HYfLn;0zTmMg#C-Y9L7r|nM0?a0dL`ey(!R}v7s95`b z&x97Kw}~-FPF5iln256?NOO(%x{4IJYk9qdp=UOovZ*btCLbYzbB<@YL1a z-I6I8J~%!eOfKMJ1SArN8a`2(ve2uG6Vuipbgb6ha|S?xI;C^@4z0MOE~E%hE~b}C zep4CYIRUf{Qo5J;_G?aw5+Wq-{^>(K=04^yMnagEPqpQT=C|AKBt}c&^;kSUpnQ3j zbDsv42EI311o`_A#@u|Z@f%EV28ta68h+6J{(guF(7{wxV1Ohf?%cgyjmDw6R#NmP(!_3iBC zH8s&UExvA4&~as^OS`ejM_W((_L~lJ`jZaI>?=QI@Y8PCf(u9ZSWnkVe;f3LZ{EE5 zVuI|R*FddZs1Y9@pGz=+bXlorl3t}OMr%1&@woDBgFIwYqx5*DQl+bMfXvC=)|TY~ zCtNhnWeY0X`@Fn+KT2&b9!FG0N+s6G%aR1qNkzZ3blHySX`K-U_!7&sL(Ijc&H2yp z?ds_vb#kvB7x`0+3cu$rl`=<;#k34)rNrGnjoR&kcrpb85<;vlS>oEyq4*1To>#cb z>LLLdD&xVgAkfkMLyig>&DFX_2#LWx{HX_fa!;R^_lUZ0fAB%2L9d9{gmOt_!mmyY z3lBGaP&s>vf(4VI3$z;5_#y$vco$1cVqG?qVKeb|p5G;>+@i`c-S_;$c!UV|l&2Hp zz9P1Mcxp4#Cdu=-^yeQXb^v+GqHV(C$)6mRe~J5am7%nfR1cIwmNE7A_8wAv#QhQ_ zMuYr9b9$Wngu5?pZJ52%z%0=vOYCb=Kl>!n{a+(dqgRv^QOMh5>_#_u&u`DenUxx! zkm_dhnoJg1JQ{<{rmJkq(ZDrmW{h#^!vIxWuumpS+}2$--uB;1gSCx~gp^cAOwZk# z>zWt-Em{E3A$67cC4G=yh5h(XuB5vZ37fs@?cf*y{6|Raf`uegohN%=lLl@ZZrvG8 ziRqS_)p0bCAs$R)Wl!r9ZS>afVj_@Q52_4?q&fdW3({Es_CKUao2(kRgKwYQ^iq8~S&%G(f#B0UUUyN9s zK?Na8;Q#rtJKvhuSqRmoMt0rdXP`JT8*3t4d=9`!H(*2h4=~btV(GRe6&2RwEn6D) zZHiS>k=)i}(6ENZX!TT*oqlBDMks;A^(=l;%;#H=`%LK8s%u_?^m2{n&`{2dg@VWH z#FW9CecwES;+vaGoYDr{-(_+G69Uj}M;8ipDo_;7H!+hAZU07^D*8{-)W1WFu_D$M z^H?pm!k-3r`%)sdodqugB^iY zLa2l1r+jULb#dKGUe^rYkuuQ%%?#e{Gn6a*0VAWilvbW!5obGFPaGH+xVF8GQDf`s z%8P=6^7&~G0s4dRMt}~Z*(w_D(o(<;W{DX##5nB*j8?h=)*TGs<${vZB4GByrrz0I z@GbF_be|Xt)!#IdHNIwuui|&70l!NAHRpit&TiU)_HA^O$?l1!CZSV`7Va)~fqf5S z8TsWUz7ng~zAXTUIk?DPKS{CLUyn`vf3P7fjq)673c_)`KzuxMgTF9)L8UrO)!`$Q zk+o?PY~v{QkhR72xHx9Df;?*Ccerf<69?3o^^X5JyZx-7nYp>?aJHLa515w4 z((ahm#cLGj+p}h$slXUww6Wk~%Y8>nQiO$jkr00>QjF7T{|Ctk%tzT`a`H|ou1}kK zp0FCpy^2BFg&(Iy;y{k6@w2pT>KW1d{?om&&u&JYTB0fMt#sl)i&JVcmL#kDc&Lfy zui>HIBYNs8h6;}{jmm1?gzW!A@6o!=f5{!TQ&1R6WCsHkOO{m$+fN^Y)3PmT6bA2N z|J+>=f)PZ%O5i#rmEo|DBN(^GUO=Bew+}=y8Q?9_X8I0nk$m3{@2D9*^s}}bZRD${ z702EpL<~ji{O?)8%!!u8@Q_2iL%Ku5y6Hg3F-_dUB55dW?(Eo4ga17%C@S$#yV`-8 zRj=9)aHM{O(*I-%+K8c6*<9F@l<*)eg?w_Zp=6g;L(paDy*BXu(09<_0EdLcoT?gN zUY7nSlkTvVaVXC3h;ux{!igH*u!QOX7d1TK)l~HLBV+GM2Tl7db;V9J`HJw^OhBmJ z>T$hEquFS}68xf#wq6fUuk6>;HNIF~{Dh|~L2hgPV(!x1e}G|bf?+wrgc?ySl9N}6 z5GvVsONtt4kEuFW3+w@<0=u1tW{X+35^Z^T8M_@z?0se?tYQu>aBQkDab`|*g5Jrb zGlChOUGY|h+**B(d3Ihy4A4Ie0l`3pr@?v9oSU0ltI94l^B5!8ba01m#3w3h7K5uY z9hEL}N*nEm?|r=!-`wvULkYn~69$CB*;9OUpYmCtD@#EKuM{T>JeJO!U< z%?1~BA8ENcdcd&Z4A^hQP|qCb^EtxwwiM`KJTva_M;6gwp}_u>^P8l@{|8AYQ4-Nf zh%Nbgu1d+UsS4{CnBwuQ|33m#8CsAZBh(2(cYZil2+)$SZn_8n=otk3J$q@1iNW8# zsex50H;zGt89J;(E3S7Wl9vQ$JP3iINAsTyJ=sm|;e^)vs`*&{S;vFq(a_eV06{MM z-Fwp;0Gl|Vuqkfv0S|UyQFS#Ry(w-xEVLVJVS*;`&+)0mvKjSXEHG{j4@3XN?)-3_ zgE)neVE2?J(*l*NGk77B%0$)OP>!AyEis`al+js^UYyg!CR3{!I%*1kaqDEiZftC{ z$PwkT**m47p@YSzDy%(XjX!zO=dDo2Rm%muK^f0$^!{2rlG}a1z1l<84?uQFpGm*V zWV|-?@)bIhn?V0Y|K};hJx>;YoEhX_jvS7qeVM2eT?Xa5wIjcpr|fr&gOpC7Fi7w( z=Ipx^$2(9M#OS!&fr%Zse!8~^6K%~Mbs`>&A^9zhaSXID)8fDvz9#^HA3ccueSOVk z8e@k|o-Gqxm9_Hvka+~0nRPcWY?bmee5wz!mEtjcSuZ+aEtnNT|Hh1ik`7dh`PN|d zXkCz|@bM%4=ps%#&TUs;<6@&w2O_6%?P$NJ`9@!?KVA4UMBN~rP@sQD6&h&f|7y85 zA%@PokAAJY3ACN$-}YHUASCizm49KymVdEgFLDW@L%uEk_Se*_=WBdf|7O;VoSD=L zU3Hw(k7gf}`K&EumFVf=-kOV307J;ZOsoKJKduR&fy=f%u zOg+MGr=qdl`d)SixaAqy*rFNxJ0eL-Tz3rKl+ut;fR2eZpa}JK8{83BlsHv$n%1V? z1Nh~_3$_O|@;_Lpt6aB28DamH%?dH0gg$l<04j+eCud`ue|x3HSPg8e8vaIy%v{U= z^yy0kF{iSoW=^$&zP^5!ZOFESU}K1kJrMiPD@*aFzGzMg)-=ML@xXr!7!7aBfumQc z@34FFf++uZ7NdS*7iXLFK>snR0RG!7hSoHTPOT{-h-gk7YN1f!>E@)!Zq@NY^~70d z@(i4xngA6xg3EF@K1k*;>VLfHnhIp~xSx;4YZEmd>ktInox?ia2Zf%D1Wc@=cC{+XWDR>z6b%r))?nh`t?VBHnB8ArWH`Zjo0!W1_dg^giBTZP zv$@})2>(gzTY<1ce11igWlNd0{102q%=rm0%iKqAfPxK}M&Hwu0~RTf**|~@$f9~T zbnqzR9owH7MY#LqFpKASV+?I#4p1&3$i^o$?2@o`RW*VXkUOnW z&%^6%vy}PVktx2q5ox?0wRZ=yB~)B3h_?Zm8Dv)HaCvOx&D>QFNmXxM%^9H~Cebr? zo##gzB~Ht7ZJnJLECZkkfZZZs(Ftg56yBUDfzz$Dc?q-(u}UC{ut@P4!h0iMjkCH^ z`!$4QpPou(Lh`CAYX1w;2n(CIImbzl5qRIGiE>3njgV0H- z;F~Z*LxS#SrLut(hYBUR=P5l038!PJ(K)I%*e}WpN4*cyk%iN}Ow;2z!vg;U_Y)q% zsPi`j4>2}2ap+J#Aq;8^6m-B=vt@RDQLwc?5Jvtns!XB)mV~Dx@^}8sJ+lH%M#L+F z2V1LaYg?kG7d6VmR5W3=zx~9ogu|CEK9`o_0C)rDlM0J8!1cm+)7XH$$73t+GAh1N z!B|>NMozG)LOjSB(_+32fgQEXia#lk7?u7~tHMn}!h`Al{a5Wc`)Z2fr8Wm0ChoLIpqe>$7NAWWO1?u|^<$e?NXJ&RSepDEaK!4;6nyw|N9L zb#(;21l#n`p~|5(Cs!ufS0Ri)|A914j13GZ02?|qJe(<)27*LiO33hZ1}2uCpRGV< z9_!yg4GF0-w>=Xmfg|Ri#tgDB+HzM>K~k9vKXqn=*z@9H{O3<7hnbO~&fZUj)zw;M z6F?*%W(Ibe^`6|-aESOO$`uM0HDG8S{$glSK%Un~j?y^uae;OjS2!>apFj9{Ml<-& zQXf6=TE~6OiXZ~Y;o;M72Z712(K?um2S~So9}F#{xR`B{2@6{VEo8|HI~QV!F7gHo z`7aq`pgDcNbH;`fRR#!OA|lu!unTX7WW=aC!t4)h4*;v_;ac;TAfh_*+eER@ki)Tm z=y=|48v##17u7|0Gy>fcfR4d+7Kst<57vM67hz+Py07FdaDn8jZ7ASqI{9`gEa<$Z zEU^_D(!~ev-Ms}IG_v$~uz$4xt85OI${4|jNpN`)9eK@b`o+_K$9S~xSb~^|%CBQM zzB)R-Xs3R=?R$P?-l}8ws9)u5eqJXX&2ZSiA`Xy{0WCuWI|EBK=`&MiK|EG2QpMMQ z@grp?-cl(p`2+)hT6}3}OxYLCXn+t$_to)EMLjBwUx#WP%aU{h5IZM)d~&SZ)zUoi z$-T*;{`cYVKU6n4dHH!~Cs7QL|I-n&8ZMe;^azQ%s={5PnL!Eomi%lB^VAg^u>L-I z>k&ZZ|08`1=mEwu-5KNt`9u^u||)Y1jB3=bk_p z8<`6yBsCB_qMMnmV4xZ91^SQ*NVMGE-j11aadpiCzEyxc#;7=HXdd9)O=SaiCxc`w zPifd_m^GD*VguwFw~CqLB9syb(8-3E>z05B(Q-@~v84>24gwy%f1y1{!R)pLOW&;r zs$13Oj-lnj(mCSN6jl-N6_ z8lxbeDpO0%&~RV@*dy)kC$FMPf~2fzMk=2&{qFG#-3up}1F>daRIzNB&vROR2-K-K z3-E}KcXXeyP!{2cH8?&;&jh9?>@6}V)(H7e4k{T1ulU;Zdfkz*o3q;@T}-v+S%g}u7B;kpZaitk zkh!3~Dp7r}I$+5i{Wq3e{jc|hN}HzPW9gqZb9gZz8^yspDKzU0R9``@>qb^xIlRL8Q64j+X*?OKft*n3dBcy;Wq|@?YdpJ&ot2gEZ^k zHA+rJ9T67^s0Im76OItbcOU%yZo=KGWg;)iD}>XZ9`U$zne#!bs3aP0!rTQ=m#?L) zWXuEt6}7+5N73$?G`K0ik4Rye$NaYl#qgcZOos9XM)=>slj186@X~-^?*;fLsG^$X zi4qGnuYBk+Huy{w5`JLl1GGKQj@H8~e!tFFFHbOAs)|uc^Epwz;|w{p93Y@G2&R)5 z0w_Ck;j52i#71*p*iJ40Z*<$WisgTwiN*du{8SHW)V;g}HHr-)Q3+Y8L_`dO(blIP zm-#?8`m2l#^ze3jhL!ZldZY2b)E*QTH;IOZw6y(dK?g|4!aBL`zC-mGnvhl2G!D`* z|0D3B8PsLwP(*sUzwe}`sc8zP0(BK7zZ328&RjG)DUWE#e3dtv^xp|)z)gYzGeF2y zu!csoF$yGI8rg%i4_*e1HQd>rjlaED@_#03g;(At zW&KpJv#-!mQB}><5hUsTrI|IEYcaDgv#799V592_8V-V(vfSLeINm!x!AGHz-H4)# z*;HU>gZZrc^&BvS)KkLGyOt{|D!O{n1m$-B#(p(pCv9Lbdn=RuRS6p&hx!fs`pJPt zcF_a0Ym;8Bu6_*lecH_VU@!$0-Gn0*u3bd~pov?;MnH@)|FC4SKV0o6s@Odks8FDF ztBb*)Q1f+%BDP_!SRKTrU}2H9zY6jH@w-c2`@+z)!;@b$X|9LWe8RL$2uLV7QvL0{ zTA>s!(6+(2D7*#~_c;H&C6TpuxgaGWal zI2qhB(r{u899t%oAm-j#xC@VhE3E&5(8j`V@!3TbzIk}xgaqD!TjP7=oKq z96cKcQUVC#8yg$pE+}{?(E^jiSVQDtzm}fS$M0dY)vIO$_Zy39iY*hQ5`#*P7MyBK zpiKbQth}IGYZi99a2lHh-~1UD4+05%J$|#B(8EMk_x}Ok@aliTx7$4b8xhYFO4896 ztC2U}9U>hrpOCQxulEY!1c|Dc;A`9I2gb}IE3G7VF%bz5-h0Q%g~V5wD&xEPhkai7 z!DzK+mv`}}`mDd2f(cjeN4}Km=O5LJAL4 zxb$M=)^~(z{Ql+@7t@06xo_=vGn3kXw=a6Y(haxgUX<8)9aZMYWmdNzBAHu>S-GuQeeW0Z=d>X47Yi_=KE+a7j!g{_mN?IJLyaXZfUWmwjfQI ze!YP*vx<+<)k*q+M;@nP85FbXE7Xn4Vk*MPAVOB{UGslFC&E#>Gj)1p&A`riW?b-9 zn_Lyg{%Q}R_!r9g_7jANz!TogF@Gj@m3FYwQXm2jahTwChPICfmy z3kllZWH8>RNk)VAOy;J?`O3y_{ebJ+L!NoDfY34X0r3#Z``ZI>>+ap)xll9U&-%=F z7FNxDUuF!=`%Pg)Vk|C4X*!(Boz7NZR!vUo0CR@1g9H1I%2%&mebyE2Bruwc{B(UMz4iNSGsf3??g%eMa=Mm0M2(A6I!zk7x>rukM=o%{v4N1-UnfB<9#;roEOSj3wY zOj%FapZQJkth zJz=!cVS0n8wh0&y@VHvF&P*t%s37$*JEJrBETYrv(p31GScr1hi!W*@8YCp_Tzmc8*tnZKQ2Sy7O-Ohs4)Zr52nzDL zV>UqXHfDvw9X7;sm{#!2uPfe_kOt2WE=J#P9gTf#5;y%Y7@bt2mqkM6mTCMlr04f8 zH+Jup<@S-&H+6L5Zg|=y@3K=lBMhn)NGXKfwlbo&HuoA9?3<)#9uhp+lrsTjt5Ty_ut`rG{H~C*-bZVnTVtm?828`&l$EgqT+uPegGY`po}w=o%xqm}@@aNnhxbVgbC^+t(4$ z6Nb0@7wT4-fQ$keIl2AGw3L+a>1jRS(J?YJgZDhyZtqVOn0Mf#iDAK(6lh}0;Jq5w zM*IO0yDz*gyN5(3Tsl=F^@y2S@q*QWWjYYQEa8G#cstQZPIPFKVb5xwbxC($En5_X z!jVRS)=OwYW?o*m`JQ7_b~dfebTxJ&tG<|{qa)D++Dt7@V;HCM{<9?ZjG;MU@E9vY z<9c(*Jbr%7$?o}kW%aAEIFxSs@i^%bh2d33N7E0GcJaacHPhlC(o@t#G@MlLwZMw`}SeG7l8Bz)| z623_cFA-f7j8*sJ=Fb<03Wm1ZB!>cbcCRaNsexcf6D9@B!jjO{l>=1w>C;O9t;EG& zW%h}&K!6T?n|)RmdEwgb zpZ?yrnlja!5EigtA&hw-VFT^rc#HHzn{j*uykTWx%cZ=cf~hAq#3XTiQC)g2#xVGB zbFj{oX>Y-^&P?>gOS9J?iVr)7IhYSSc)83cml;rUDqJA5PRz@zS^lL0BQ-S)K0bc; z0=A0H!NUtTsp+S%CLgc$d$`QsdE)zB=^XoX3F5mawBy>@TUtI+jM~xmH}qJij<8&@tX{AGM28OSmbuSLw3OAxvanK|3lC6oPxDwFX? zK4cx)^d|YDcDeq8E)MSt=&J$DXfcBC{wkCk*$vZAx@D-w?;A+Yci&}8dMmiM*?j#G z&kO5<+;h+tkp7vn=H?7QT49)`=sr5UMnDM-@!C`I>pJj*f+QjK)bZM#_UoRmswH=C z1Aue)%Y9evEEI2tIQ8L+?$sAh+K&!_2t!@Lkoon?1~xMc8M5Z8BkYdFjOGB%ydE1W z7KmoDLuMSXTE-F>AjrsmI3x0V?g2bb59ROSihQeWx{4xSwRgTZuy|Mcjz_RPW3)VY z4hgVS05g>)MHPrN&0+&}kOpWHeL@Z_O?VR-uALO`=?8i2^=6BmJML9=Mm7*`yn((l zA`I~s^sK8941kwhjC@r16)*hgJKbSzS%Z75Ay{kW8p&|L!Q|5!B}90_)RVQ{vav?F zt_eiG5Jr*-8y-N6F@;xw*K$m3y&DV+Dc|EpB?!POwXCvE^%F#=B8Eh5Y8?B@1;GfC z+bo_Gr#AO41((p9zmSowKk9_zduor#ga~4{+{(g2)WwAd5bxq)W!@Jqzz-{`q9MUW z{J0t`ZzN4Z9I)R}HR@T<}D?EjTg)ku7u(oscu9l&?>QiBl zLvGENhh46aUewa`0YTGDuTlE4VatN^po=}H&iii8=T)DLI=q%1r=ZG*A#nDNz(C;o zQ`U%oep#)$4p2I0UD$8VxmPExG9vehiwblGyk3(Jxkf%dSS=-i%N~gF?B@MsX3w6% zLV)Am5omqR&dxuUzP`4wU%lHafmaW<1&FdvtlPFk03(H5xCTxJv~pG|nid9GIHdd(q{ zxyZRJB0|C|EY;I#AtKUiF1WIoK6LP@xH#HEcw?-P^Y9cBa!6A_MtK8pfmF{OL4!r2 zD>q^m%E!A0t$*SVm^qm5cc1!CaqPpJO}~as^FoRZtwY6zbr9`lZFWW^vW`?zI&Ve9 z_WWPHeRn*S{r~^5Ldwo4TO=8gB0C)`BuODFk|ZLVlNH$=N{Bkj)-X~bn~aoA2wB-H zBYS*b*J<3{cc1(Ibbr6U$Il-wV4V`?=l}G~?fm`amDeChRrpzKNO%Rpz)Ly$uz1O`IcRa|doylDyu3lVol3xGxw>68bK6=*)*%)unG64 zT7n(jbTo~`0t^%{ZMuWLjJI*`-TTJM-Nr`fs=YlhAwJ>ZG*FVpDCIomTizES|FW-Y zhBL?-K;~vrIUaKCl`V?k@+!}Fzh_1Nx~%{Cbbo7^p{|HkUVZ<72YE6veXR#=2E&v> z5s#+g)*Bb}XBSo47Z3FxI|GoUU^g$9&ETco6Yi_QK2UAvkY!r26-j46AET^iUXk(1 zle9=n2Le#Qm$HpniJ{7$Z$U%XtL5-L?A_fv*C*-DdtNDeKfZE26U^oB-<=JO8=qY=T$A_kY$hz!o674z3m*F`u?ZNYxY zg(mZcVj~t=3=^c@2y!$vG%COVn`zy7D1b>?&K?uMOBIJTVT}+?Z|ieAiy5q|${rmG zll;&{u@*pC(#*c|Ytd35PME7>KWTFA=(op)5VtdrIu@ucofdmo3RRUzr zcku}9O>@2c!eXg?fsTeL8hyL#!wV}gSh1em#z_nsf=NziV?)COz5`~xOVk={vJ_!O zMvDgsT~1Yb3@?6p6-V&djUyD97d`U3GazeaV^fI~%)Q9zxID07nAN*ygndfcqQ_6Z zX}>^o*)-Qip7nq%g*m2Qe1kn?;Y-Kj>!J_bpX|FJtb8v(JM!qgfbv;Axl;_nP3lQ< zg#9g{RQ&_z`1O;7*k~Ge!%6g8TJnne7;{2Ffsmh{KcXU7>?T#?r~WV2?97f)M*{Ay zI14uS8%*h-7OXl}IpTv{z8!~ola>c)?G|hUDntHKpvJ@8=926859_~FgWy9mD zw6)6r;_S>TA})<*`!5$#)DP%88{WcYyhTcnmPhFr4>c;Rt<$XlOd=+>0ARUQ-_kNo zNLZ1A)713+9%QEnbiBy+TKHvc0!Ye2KoZYGiwWDnRsU&a=s1YiJ!52)es%)-oVrc@ zgK+~1fS|VGuzQ^pmq5*IdNC#G{>Z~}lv&E)^}ReT=2*a|f(E%Ka@y4*1sVmNaz_r! z0%(fwUDh8sYOh5tnpP+b=xK{uefzoFOdrJF(17qsKk!^2+2VD3U)c<24Ca)ODAS8B zHs?g`{fDCFN>@SKY!3|yK}(Vx9ay4fzMVmsxCe$hd9C~E$0othSjkFCMpx}(y+?4@ zcbn4DR7m=l)7;4__Zv7Ww!9SMkWX5|eQwn77`!{;NH=}%zM-q9oVc>Jm^7;=ixhF5 z->K(p;sxh<#7uZw`j+m-;Akup>}y{LZE+)dyQ{O2Nvu!OuyASCXNp7k)5|Z< zZxMq%(tqzGE5(CsQJI;ePggsq+wC@#Qf8%PuR1(%Ub-=x+XvpN)+DkSOatX;FaI+% zy7XDcWeJb9+}T{n{N%bFTwxC%M27K^0`JI@asWpbZv*dNkXrEbESY>%P8oAymrY3P zdh9Dun_T$n?GGX-HwS^XQ* zY~CsSfK^Cfh{`*vh9bDvx;(a-C0ZbHM)Dw;|$?9K1w0d=6ST|V-5y5v%M+B zo|G2ua__r_03;IR1fIrGedya5PU$^tukRjvAO+|sG}_w|9+OC1>cfXec^Z(B2Ht81 z@_D7CD+gD!UXba za;+avg%9)4D9VdqSYv+Mm)^Ovo@INkG1|HIYTxNjNVQBlwey6CYXW1!!TO``jES4e zimxvXB~Xvvr9M!k+n_}`f5fSi(>ki}@McdKV}hroDVPmy*H;_?CHj_USUq;c00|I7imTN<=Q!X%UsWeKCEye`Ev{k?bdkgC>F|t-JOkNpBe6;O4wX0 zF{z^1^47oVZ7`m)Vtl&%`I~GEAl6y_Q-=&WH~G|_eq!7_S-9}-Nw=ITGA2>=o!+Uu<3BQD?P(F-v_3D~lIiEvO(n#GOG8g8`Uo(_m zM)j;1ORVkFw(w-HUUkeZKb}!nTlX#rr!USDm?ZL?do@J?Zru+9<|!_s(VSVj0`0FH z_r1?B#sW{2F`1W_hcIKRs^m&aN}1sM1WPp?Ox_7QZZ+Nnon2(-O)5M+B_-vlwz* zGafwr>K8oNbcT5nnO!9GEu9dEhVgowTx9Z1Lx`#`duEcy*R1}TsVXu(f=&#fCWJhp z1ZHVJSe=M$;$mWgzyf>@h$?EIVT;0_zF}sWEK=HKe;to|&z9Glo4bmZvn-B|RlA;B zEG*_#yFJ`C+gqv_Uw{#4kkCB8!$hNQEbJ-`wQg+wmi0P<0?$dbN>)I?2*uw}R@ICN ztilO{W@6xM;*kZtIA;atO^+RGpT;16{cXhO5_S@``N*zk^WifyDF4%gQVf`6h)-po zsH+F2EVFa+qLr0)B=@y97pvZ0TxQx{4ajMgmn3Z|QTkh0r7a#nG9Io)fDGd;SH z#g6N^-c~y<%C{ElCqNa6e*@CV)|nXsVZnnBeSL|cjFS&knLm!wlbIDur+lRv;{Dpw zppFi)-8@TTQXIL?4ooP{e8#u)tiFEO?E&}5-Y6qCa(KbW<_zjFn8mp6w2{1WNtK)W zEdl0G=}ShG^wU>IS6-VlfA<2wSTc15`1tu_Y~3@5hvVb9kdS?T{#jMrJjox2PVu9& zJO@R2YaXeK!FZ8c(;Px|$#ckE>o2^>DaRwm+raM`C_I=wQw+(Rty=Um0cJziz#ti3kMv|=;r_|n>UN1!$G_5Di&zNFp z%rs9cNf{wiY6)5T;6HF3B!1Z8&f>aHNK*3SRB=`0qryl2%9_!SvrsDkw4-qUGq`H< z3pNn-hTA#_5%I}yNG~zIQ^&bDnRsfq$ONq4lo@&HvF6WqBrqT(IXj!KqoV^5_23$2 zlkgI~YU zQJU(pgdDq+lR)Car`jL8Z5p1ToO#ptsWa9^c|1KR*mow&{R@I=$LH60ZWME*S>T`Y zAvXq&W>r5%iO8LH-BxmVJ)hxzAjLkQ*mAy8Z2f;wY+`yqb+O2K7`S!~4P7uObklL| zdQH< zd?7kt>Ti$XR5oxe)I{%O50d?uZPFK<&ylW{-}Xl2-j=X!s5**#oiB&Cd8b^rUIRWm z`tI8P_n)3n?hVY@a6zR^Omc#~K3@2sm>7qY)Gl~JaaME`U6Z0@k$AwjFDBsIrK>Zb z4Gl<@l}d_wG$a;feq?mT>l3c>Yr1xxY%2BILnLpxc1*KZIWO(+NAZ6|Bx zTh@%;&v!l-T4|!i^jiLomBWa9n1Rf#Qi3T4LhwM-@$L6GqaZGKdRd6$%vY9<{#i?b z?ZDr%bo9}1W(4$~lL0>$)A{`6%Pn0uTK0e4YWd7V-QGExCmEu0ryW}yH(n50&K3}v z7=^84c-74Yhn8a_j+?cLcVVNXj7k%li~UFr3~^h0Fwi|I5Pc3uiDI(-h^{|DC0qPu z52nePCW=&62PpA(diS=wEyf82hPcs$h~A7np^uFdH_(DYulnh|yWQb-SvZ>q+~c=w*j){rf6$)50a ztqHkshNxd4NGguc^v4C%-_^=PThX!gb#1U7~4VkctDP!XoJ z9GN*MJJ21c33+AooF1g7{-XK0Nm5o-$vu^;|G^W|eP#`laX+sRl5|L-90=D;?qufD3 z0nE;L`j?-Hv3;91R5AuF5v{BYBG!`XRyc*O*^?Y1jqa=BY~D+i-22z-lf2&gWi8$; zv(KcN9r6mA+Foy(Wda;5(h+g%)~$k zLC9T&!bo#{U}9zNGe&F@V(7H*#zypO$8A9HkUq}pZ}L37x(FQwP7YPsh9uL!*@ zSx%Gs_*pbK=0N@g@er~;d^Je_j@xJaz~$md0`ltDL+J6GUhHaDJ<3HUOo`&cJLT50 z8oRiI;^Jmd=n?aw^YTl(&2-(*bn{e;3E9&M9FW|%>~p5s?0RVz=O-#Vvul$|Y1OT7 zd79Gvuu{`A^S7B-dW!JB`Wf!QA#cPV^5N0~whYOY8*7q@7XBYu)dCrNI(dj*?RjCk zaP@Vdg!y6kLm=+UQS%wD+EXjs9LKwtIAqz9<8{XR6?JuVcJvPr8LeDUASWkJO-()V zRdd1*2{x=$b)a8}j{3ltY5nBG16_|%6K&5Hj}D~)U4yfAhZP~NUd7bYt~IF%Kg`N% z9DXVN)>Qp~X1T_&h9Hb8`Veb!6$Nh>+r$z=uSd%gU43AziVHXWA$3-zq^6QV+zjT! zr*G{+BuM;_ly^-tzDp9=c4n9mSM_2lf47Wr6jDt(+q_5Ua=hQt>YjN-?+jpy-tqYP z^HWh#xxUmQGHN8yDthMI2L7W8`kmmLumvf!P0&eIHP8lGnGhh=t9mO3EmRzl<@szq!Q&Rx(5omB~m!etQ#psObGtg8@C? zgS@}ee5}0s44WdRc>T5SON$(QmY_U&@-HQp?E2&tst^?MhYFT#

} summary instance + */ + write(options) { + return __awaiter(this, void 0, void 0, function* () { + const overwrite = !!(options === null || options === void 0 ? void 0 : options.overwrite); + const filePath = yield this.filePath(); + const writeFunc = overwrite ? writeFile : appendFile; + yield writeFunc(filePath, this._buffer, { encoding: 'utf8' }); + return this.emptyBuffer(); + }); + } + /** + * Clears the summary buffer and wipes the summary file + * + * @returns {Summary} summary instance + */ + clear() { + return __awaiter(this, void 0, void 0, function* () { + return this.emptyBuffer().write({ overwrite: true }); + }); + } + /** + * Returns the current summary buffer as a string + * + * @returns {string} string of summary buffer + */ + stringify() { + return this._buffer; + } + /** + * If the summary buffer is empty + * + * @returns {boolen} true if the buffer is empty + */ + isEmptyBuffer() { + return this._buffer.length === 0; + } + /** + * Resets the summary buffer without writing to summary file + * + * @returns {Summary} summary instance + */ + emptyBuffer() { + this._buffer = ''; + return this; + } + /** + * Adds raw text to the summary buffer + * + * @param {string} text content to add + * @param {boolean} [addEOL=false] (optional) append an EOL to the raw text (default: false) + * + * @returns {Summary} summary instance + */ + addRaw(text, addEOL = false) { + this._buffer += text; + return addEOL ? this.addEOL() : this; + } + /** + * Adds the operating system-specific end-of-line marker to the buffer + * + * @returns {Summary} summary instance + */ + addEOL() { + return this.addRaw(os_1.EOL); + } + /** + * Adds an HTML codeblock to the summary buffer + * + * @param {string} code content to render within fenced code block + * @param {string} lang (optional) language to syntax highlight code + * + * @returns {Summary} summary instance + */ + addCodeBlock(code, lang) { + const attrs = Object.assign({}, (lang && { lang })); + const element = this.wrap('pre', this.wrap('code', code), attrs); + return this.addRaw(element).addEOL(); + } + /** + * Adds an HTML list to the summary buffer + * + * @param {string[]} items list of items to render + * @param {boolean} [ordered=false] (optional) if the rendered list should be ordered or not (default: false) + * + * @returns {Summary} summary instance + */ + addList(items, ordered = false) { + const tag = ordered ? 'ol' : 'ul'; + const listItems = items.map(item => this.wrap('li', item)).join(''); + const element = this.wrap(tag, listItems); + return this.addRaw(element).addEOL(); + } + /** + * Adds an HTML table to the summary buffer + * + * @param {SummaryTableCell[]} rows table rows + * + * @returns {Summary} summary instance + */ + addTable(rows) { + const tableBody = rows + .map(row => { + const cells = row + .map(cell => { + if (typeof cell === 'string') { + return this.wrap('td', cell); + } + const { header, data, colspan, rowspan } = cell; + const tag = header ? 'th' : 'td'; + const attrs = Object.assign(Object.assign({}, (colspan && { colspan })), (rowspan && { rowspan })); + return this.wrap(tag, data, attrs); + }) + .join(''); + return this.wrap('tr', cells); + }) + .join(''); + const element = this.wrap('table', tableBody); + return this.addRaw(element).addEOL(); + } + /** + * Adds a collapsable HTML details element to the summary buffer + * + * @param {string} label text for the closed state + * @param {string} content collapsable content + * + * @returns {Summary} summary instance + */ + addDetails(label, content) { + const element = this.wrap('details', this.wrap('summary', label) + content); + return this.addRaw(element).addEOL(); + } + /** + * Adds an HTML image tag to the summary buffer + * + * @param {string} src path to the image you to embed + * @param {string} alt text description of the image + * @param {SummaryImageOptions} options (optional) addition image attributes + * + * @returns {Summary} summary instance + */ + addImage(src, alt, options) { + const { width, height } = options || {}; + const attrs = Object.assign(Object.assign({}, (width && { width })), (height && { height })); + const element = this.wrap('img', null, Object.assign({ src, alt }, attrs)); + return this.addRaw(element).addEOL(); + } + /** + * Adds an HTML section heading element + * + * @param {string} text heading text + * @param {number | string} [level=1] (optional) the heading level, default: 1 + * + * @returns {Summary} summary instance + */ + addHeading(text, level) { + const tag = `h${level}`; + const allowedTag = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].includes(tag) + ? tag + : 'h1'; + const element = this.wrap(allowedTag, text); + return this.addRaw(element).addEOL(); + } + /** + * Adds an HTML thematic break (
) to the summary buffer + * + * @returns {Summary} summary instance + */ + addSeparator() { + const element = this.wrap('hr', null); + return this.addRaw(element).addEOL(); + } + /** + * Adds an HTML line break (
) to the summary buffer + * + * @returns {Summary} summary instance + */ + addBreak() { + const element = this.wrap('br', null); + return this.addRaw(element).addEOL(); + } + /** + * Adds an HTML blockquote to the summary buffer + * + * @param {string} text quote text + * @param {string} cite (optional) citation url + * + * @returns {Summary} summary instance + */ + addQuote(text, cite) { + const attrs = Object.assign({}, (cite && { cite })); + const element = this.wrap('blockquote', text, attrs); + return this.addRaw(element).addEOL(); + } + /** + * Adds an HTML anchor tag to the summary buffer + * + * @param {string} text link text/content + * @param {string} href hyperlink + * + * @returns {Summary} summary instance + */ + addLink(text, href) { + const element = this.wrap('a', text, { href }); + return this.addRaw(element).addEOL(); + } +} +const _summary = new Summary(); +/** + * @deprecated use `core.summary` + */ +exports.markdownSummary = _summary; +exports.summary = _summary; +//# sourceMappingURL=summary.js.map + +/***/ }), + +/***/ 5278: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +// We use any as a valid input type +/* eslint-disable @typescript-eslint/no-explicit-any */ +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.toCommandProperties = exports.toCommandValue = void 0; +/** + * Sanitizes an input into a string so it can be passed into issueCommand safely + * @param input input to sanitize into a string + */ +function toCommandValue(input) { + if (input === null || input === undefined) { + return ''; + } + else if (typeof input === 'string' || input instanceof String) { + return input; + } + return JSON.stringify(input); +} +exports.toCommandValue = toCommandValue; +/** + * + * @param annotationProperties + * @returns The command properties to send with the actual annotation command + * See IssueCommandProperties: https://github.com/actions/runner/blob/main/src/Runner.Worker/ActionCommandManager.cs#L646 + */ +function toCommandProperties(annotationProperties) { + if (!Object.keys(annotationProperties).length) { + return {}; + } + return { + title: annotationProperties.title, + file: annotationProperties.file, + line: annotationProperties.startLine, + endLine: annotationProperties.endLine, + col: annotationProperties.startColumn, + endColumn: annotationProperties.endColumn + }; +} +exports.toCommandProperties = toCommandProperties; +//# sourceMappingURL=utils.js.map + +/***/ }), + +/***/ 4087: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.Context = void 0; +const fs_1 = __nccwpck_require__(7147); +const os_1 = __nccwpck_require__(2037); +class Context { + /** + * Hydrate the context from the environment + */ + constructor() { + var _a, _b, _c; + this.payload = {}; + if (process.env.GITHUB_EVENT_PATH) { + if ((0, fs_1.existsSync)(process.env.GITHUB_EVENT_PATH)) { + this.payload = JSON.parse((0, fs_1.readFileSync)(process.env.GITHUB_EVENT_PATH, { encoding: 'utf8' })); + } + else { + const path = process.env.GITHUB_EVENT_PATH; + process.stdout.write(`GITHUB_EVENT_PATH ${path} does not exist${os_1.EOL}`); + } + } + this.eventName = process.env.GITHUB_EVENT_NAME; + this.sha = process.env.GITHUB_SHA; + this.ref = process.env.GITHUB_REF; + this.workflow = process.env.GITHUB_WORKFLOW; + this.action = process.env.GITHUB_ACTION; + this.actor = process.env.GITHUB_ACTOR; + this.job = process.env.GITHUB_JOB; + this.runNumber = parseInt(process.env.GITHUB_RUN_NUMBER, 10); + this.runId = parseInt(process.env.GITHUB_RUN_ID, 10); + this.apiUrl = (_a = process.env.GITHUB_API_URL) !== null && _a !== void 0 ? _a : `https://api.github.com`; + this.serverUrl = (_b = process.env.GITHUB_SERVER_URL) !== null && _b !== void 0 ? _b : `https://github.com`; + this.graphqlUrl = + (_c = process.env.GITHUB_GRAPHQL_URL) !== null && _c !== void 0 ? _c : `https://api.github.com/graphql`; + } + get issue() { + const payload = this.payload; + return Object.assign(Object.assign({}, this.repo), { number: (payload.issue || payload.pull_request || payload).number }); + } + get repo() { + if (process.env.GITHUB_REPOSITORY) { + const [owner, repo] = process.env.GITHUB_REPOSITORY.split('/'); + return { owner, repo }; + } + if (this.payload.repository) { + return { + owner: this.payload.repository.owner.login, + repo: this.payload.repository.name + }; + } + throw new Error("context.repo requires a GITHUB_REPOSITORY environment variable like 'owner/repo'"); + } +} +exports.Context = Context; +//# sourceMappingURL=context.js.map + +/***/ }), + +/***/ 5438: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.getOctokit = exports.context = void 0; +const Context = __importStar(__nccwpck_require__(4087)); +const utils_1 = __nccwpck_require__(3030); +exports.context = new Context.Context(); +/** + * Returns a hydrated octokit ready to use for GitHub Actions + * + * @param token the repo PAT or GITHUB_TOKEN + * @param options other options to set + */ +function getOctokit(token, options, ...additionalPlugins) { + const GitHubWithPlugins = utils_1.GitHub.plugin(...additionalPlugins); + return new GitHubWithPlugins((0, utils_1.getOctokitOptions)(token, options)); +} +exports.getOctokit = getOctokit; +//# sourceMappingURL=github.js.map + +/***/ }), + +/***/ 7914: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.getApiBaseUrl = exports.getProxyFetch = exports.getProxyAgentDispatcher = exports.getProxyAgent = exports.getAuthString = void 0; +const httpClient = __importStar(__nccwpck_require__(6255)); +const undici_1 = __nccwpck_require__(1773); +function getAuthString(token, options) { + if (!token && !options.auth) { + throw new Error('Parameter token or opts.auth is required'); + } + else if (token && options.auth) { + throw new Error('Parameters token and opts.auth may not both be specified'); + } + return typeof options.auth === 'string' ? options.auth : `token ${token}`; +} +exports.getAuthString = getAuthString; +function getProxyAgent(destinationUrl) { + const hc = new httpClient.HttpClient(); + return hc.getAgent(destinationUrl); +} +exports.getProxyAgent = getProxyAgent; +function getProxyAgentDispatcher(destinationUrl) { + const hc = new httpClient.HttpClient(); + return hc.getAgentDispatcher(destinationUrl); +} +exports.getProxyAgentDispatcher = getProxyAgentDispatcher; +function getProxyFetch(destinationUrl) { + const httpDispatcher = getProxyAgentDispatcher(destinationUrl); + const proxyFetch = (url, opts) => __awaiter(this, void 0, void 0, function* () { + return (0, undici_1.fetch)(url, Object.assign(Object.assign({}, opts), { dispatcher: httpDispatcher })); + }); + return proxyFetch; +} +exports.getProxyFetch = getProxyFetch; +function getApiBaseUrl() { + return process.env['GITHUB_API_URL'] || 'https://api.github.com'; +} +exports.getApiBaseUrl = getApiBaseUrl; +//# sourceMappingURL=utils.js.map + +/***/ }), + +/***/ 3030: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.getOctokitOptions = exports.GitHub = exports.defaults = exports.context = void 0; +const Context = __importStar(__nccwpck_require__(4087)); +const Utils = __importStar(__nccwpck_require__(7914)); +// octokit + plugins +const core_1 = __nccwpck_require__(6762); +const plugin_rest_endpoint_methods_1 = __nccwpck_require__(3044); +const plugin_paginate_rest_1 = __nccwpck_require__(4193); +exports.context = new Context.Context(); +const baseUrl = Utils.getApiBaseUrl(); +exports.defaults = { + baseUrl, + request: { + agent: Utils.getProxyAgent(baseUrl), + fetch: Utils.getProxyFetch(baseUrl) + } +}; +exports.GitHub = core_1.Octokit.plugin(plugin_rest_endpoint_methods_1.restEndpointMethods, plugin_paginate_rest_1.paginateRest).defaults(exports.defaults); +/** + * Convience function to correctly format Octokit Options to pass into the constructor. + * + * @param token the repo PAT or GITHUB_TOKEN + * @param options other options to set + */ +function getOctokitOptions(token, options) { + const opts = Object.assign({}, options || {}); // Shallow clone - don't mutate the object provided by the caller + // Auth + const auth = Utils.getAuthString(token, opts); + if (auth) { + opts.auth = auth; + } + return opts; +} +exports.getOctokitOptions = getOctokitOptions; +//# sourceMappingURL=utils.js.map + +/***/ }), + +/***/ 5526: +/***/ (function(__unused_webpack_module, exports) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.PersonalAccessTokenCredentialHandler = exports.BearerCredentialHandler = exports.BasicCredentialHandler = void 0; +class BasicCredentialHandler { + constructor(username, password) { + this.username = username; + this.password = password; + } + prepareRequest(options) { + if (!options.headers) { + throw Error('The request has no headers'); + } + options.headers['Authorization'] = `Basic ${Buffer.from(`${this.username}:${this.password}`).toString('base64')}`; + } + // This handler cannot handle 401 + canHandleAuthentication() { + return false; + } + handleAuthentication() { + return __awaiter(this, void 0, void 0, function* () { + throw new Error('not implemented'); + }); + } +} +exports.BasicCredentialHandler = BasicCredentialHandler; +class BearerCredentialHandler { + constructor(token) { + this.token = token; + } + // currently implements pre-authorization + // TODO: support preAuth = false where it hooks on 401 + prepareRequest(options) { + if (!options.headers) { + throw Error('The request has no headers'); + } + options.headers['Authorization'] = `Bearer ${this.token}`; + } + // This handler cannot handle 401 + canHandleAuthentication() { + return false; + } + handleAuthentication() { + return __awaiter(this, void 0, void 0, function* () { + throw new Error('not implemented'); + }); + } +} +exports.BearerCredentialHandler = BearerCredentialHandler; +class PersonalAccessTokenCredentialHandler { + constructor(token) { + this.token = token; + } + // currently implements pre-authorization + // TODO: support preAuth = false where it hooks on 401 + prepareRequest(options) { + if (!options.headers) { + throw Error('The request has no headers'); + } + options.headers['Authorization'] = `Basic ${Buffer.from(`PAT:${this.token}`).toString('base64')}`; + } + // This handler cannot handle 401 + canHandleAuthentication() { + return false; + } + handleAuthentication() { + return __awaiter(this, void 0, void 0, function* () { + throw new Error('not implemented'); + }); + } +} +exports.PersonalAccessTokenCredentialHandler = PersonalAccessTokenCredentialHandler; +//# sourceMappingURL=auth.js.map + +/***/ }), + +/***/ 6255: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +/* eslint-disable @typescript-eslint/no-explicit-any */ +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.HttpClient = exports.isHttps = exports.HttpClientResponse = exports.HttpClientError = exports.getProxyUrl = exports.MediaTypes = exports.Headers = exports.HttpCodes = void 0; +const http = __importStar(__nccwpck_require__(3685)); +const https = __importStar(__nccwpck_require__(5687)); +const pm = __importStar(__nccwpck_require__(9835)); +const tunnel = __importStar(__nccwpck_require__(4294)); +const undici_1 = __nccwpck_require__(1773); +var HttpCodes; +(function (HttpCodes) { + HttpCodes[HttpCodes["OK"] = 200] = "OK"; + HttpCodes[HttpCodes["MultipleChoices"] = 300] = "MultipleChoices"; + HttpCodes[HttpCodes["MovedPermanently"] = 301] = "MovedPermanently"; + HttpCodes[HttpCodes["ResourceMoved"] = 302] = "ResourceMoved"; + HttpCodes[HttpCodes["SeeOther"] = 303] = "SeeOther"; + HttpCodes[HttpCodes["NotModified"] = 304] = "NotModified"; + HttpCodes[HttpCodes["UseProxy"] = 305] = "UseProxy"; + HttpCodes[HttpCodes["SwitchProxy"] = 306] = "SwitchProxy"; + HttpCodes[HttpCodes["TemporaryRedirect"] = 307] = "TemporaryRedirect"; + HttpCodes[HttpCodes["PermanentRedirect"] = 308] = "PermanentRedirect"; + HttpCodes[HttpCodes["BadRequest"] = 400] = "BadRequest"; + HttpCodes[HttpCodes["Unauthorized"] = 401] = "Unauthorized"; + HttpCodes[HttpCodes["PaymentRequired"] = 402] = "PaymentRequired"; + HttpCodes[HttpCodes["Forbidden"] = 403] = "Forbidden"; + HttpCodes[HttpCodes["NotFound"] = 404] = "NotFound"; + HttpCodes[HttpCodes["MethodNotAllowed"] = 405] = "MethodNotAllowed"; + HttpCodes[HttpCodes["NotAcceptable"] = 406] = "NotAcceptable"; + HttpCodes[HttpCodes["ProxyAuthenticationRequired"] = 407] = "ProxyAuthenticationRequired"; + HttpCodes[HttpCodes["RequestTimeout"] = 408] = "RequestTimeout"; + HttpCodes[HttpCodes["Conflict"] = 409] = "Conflict"; + HttpCodes[HttpCodes["Gone"] = 410] = "Gone"; + HttpCodes[HttpCodes["TooManyRequests"] = 429] = "TooManyRequests"; + HttpCodes[HttpCodes["InternalServerError"] = 500] = "InternalServerError"; + HttpCodes[HttpCodes["NotImplemented"] = 501] = "NotImplemented"; + HttpCodes[HttpCodes["BadGateway"] = 502] = "BadGateway"; + HttpCodes[HttpCodes["ServiceUnavailable"] = 503] = "ServiceUnavailable"; + HttpCodes[HttpCodes["GatewayTimeout"] = 504] = "GatewayTimeout"; +})(HttpCodes || (exports.HttpCodes = HttpCodes = {})); +var Headers; +(function (Headers) { + Headers["Accept"] = "accept"; + Headers["ContentType"] = "content-type"; +})(Headers || (exports.Headers = Headers = {})); +var MediaTypes; +(function (MediaTypes) { + MediaTypes["ApplicationJson"] = "application/json"; +})(MediaTypes || (exports.MediaTypes = MediaTypes = {})); +/** + * Returns the proxy URL, depending upon the supplied url and proxy environment variables. + * @param serverUrl The server URL where the request will be sent. For example, https://api.github.com + */ +function getProxyUrl(serverUrl) { + const proxyUrl = pm.getProxyUrl(new URL(serverUrl)); + return proxyUrl ? proxyUrl.href : ''; +} +exports.getProxyUrl = getProxyUrl; +const HttpRedirectCodes = [ + HttpCodes.MovedPermanently, + HttpCodes.ResourceMoved, + HttpCodes.SeeOther, + HttpCodes.TemporaryRedirect, + HttpCodes.PermanentRedirect +]; +const HttpResponseRetryCodes = [ + HttpCodes.BadGateway, + HttpCodes.ServiceUnavailable, + HttpCodes.GatewayTimeout +]; +const RetryableHttpVerbs = ['OPTIONS', 'GET', 'DELETE', 'HEAD']; +const ExponentialBackoffCeiling = 10; +const ExponentialBackoffTimeSlice = 5; +class HttpClientError extends Error { + constructor(message, statusCode) { + super(message); + this.name = 'HttpClientError'; + this.statusCode = statusCode; + Object.setPrototypeOf(this, HttpClientError.prototype); + } +} +exports.HttpClientError = HttpClientError; +class HttpClientResponse { + constructor(message) { + this.message = message; + } + readBody() { + return __awaiter(this, void 0, void 0, function* () { + return new Promise((resolve) => __awaiter(this, void 0, void 0, function* () { + let output = Buffer.alloc(0); + this.message.on('data', (chunk) => { + output = Buffer.concat([output, chunk]); + }); + this.message.on('end', () => { + resolve(output.toString()); + }); + })); + }); + } + readBodyBuffer() { + return __awaiter(this, void 0, void 0, function* () { + return new Promise((resolve) => __awaiter(this, void 0, void 0, function* () { + const chunks = []; + this.message.on('data', (chunk) => { + chunks.push(chunk); + }); + this.message.on('end', () => { + resolve(Buffer.concat(chunks)); + }); + })); + }); + } +} +exports.HttpClientResponse = HttpClientResponse; +function isHttps(requestUrl) { + const parsedUrl = new URL(requestUrl); + return parsedUrl.protocol === 'https:'; +} +exports.isHttps = isHttps; +class HttpClient { + constructor(userAgent, handlers, requestOptions) { + this._ignoreSslError = false; + this._allowRedirects = true; + this._allowRedirectDowngrade = false; + this._maxRedirects = 50; + this._allowRetries = false; + this._maxRetries = 1; + this._keepAlive = false; + this._disposed = false; + this.userAgent = userAgent; + this.handlers = handlers || []; + this.requestOptions = requestOptions; + if (requestOptions) { + if (requestOptions.ignoreSslError != null) { + this._ignoreSslError = requestOptions.ignoreSslError; + } + this._socketTimeout = requestOptions.socketTimeout; + if (requestOptions.allowRedirects != null) { + this._allowRedirects = requestOptions.allowRedirects; + } + if (requestOptions.allowRedirectDowngrade != null) { + this._allowRedirectDowngrade = requestOptions.allowRedirectDowngrade; + } + if (requestOptions.maxRedirects != null) { + this._maxRedirects = Math.max(requestOptions.maxRedirects, 0); + } + if (requestOptions.keepAlive != null) { + this._keepAlive = requestOptions.keepAlive; + } + if (requestOptions.allowRetries != null) { + this._allowRetries = requestOptions.allowRetries; + } + if (requestOptions.maxRetries != null) { + this._maxRetries = requestOptions.maxRetries; + } + } + } + options(requestUrl, additionalHeaders) { + return __awaiter(this, void 0, void 0, function* () { + return this.request('OPTIONS', requestUrl, null, additionalHeaders || {}); + }); + } + get(requestUrl, additionalHeaders) { + return __awaiter(this, void 0, void 0, function* () { + return this.request('GET', requestUrl, null, additionalHeaders || {}); + }); + } + del(requestUrl, additionalHeaders) { + return __awaiter(this, void 0, void 0, function* () { + return this.request('DELETE', requestUrl, null, additionalHeaders || {}); + }); + } + post(requestUrl, data, additionalHeaders) { + return __awaiter(this, void 0, void 0, function* () { + return this.request('POST', requestUrl, data, additionalHeaders || {}); + }); + } + patch(requestUrl, data, additionalHeaders) { + return __awaiter(this, void 0, void 0, function* () { + return this.request('PATCH', requestUrl, data, additionalHeaders || {}); + }); + } + put(requestUrl, data, additionalHeaders) { + return __awaiter(this, void 0, void 0, function* () { + return this.request('PUT', requestUrl, data, additionalHeaders || {}); + }); + } + head(requestUrl, additionalHeaders) { + return __awaiter(this, void 0, void 0, function* () { + return this.request('HEAD', requestUrl, null, additionalHeaders || {}); + }); + } + sendStream(verb, requestUrl, stream, additionalHeaders) { + return __awaiter(this, void 0, void 0, function* () { + return this.request(verb, requestUrl, stream, additionalHeaders); + }); + } + /** + * Gets a typed object from an endpoint + * Be aware that not found returns a null. Other errors (4xx, 5xx) reject the promise + */ + getJson(requestUrl, additionalHeaders = {}) { + return __awaiter(this, void 0, void 0, function* () { + additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); + const res = yield this.get(requestUrl, additionalHeaders); + return this._processResponse(res, this.requestOptions); + }); + } + postJson(requestUrl, obj, additionalHeaders = {}) { + return __awaiter(this, void 0, void 0, function* () { + const data = JSON.stringify(obj, null, 2); + additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); + additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson); + const res = yield this.post(requestUrl, data, additionalHeaders); + return this._processResponse(res, this.requestOptions); + }); + } + putJson(requestUrl, obj, additionalHeaders = {}) { + return __awaiter(this, void 0, void 0, function* () { + const data = JSON.stringify(obj, null, 2); + additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); + additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson); + const res = yield this.put(requestUrl, data, additionalHeaders); + return this._processResponse(res, this.requestOptions); + }); + } + patchJson(requestUrl, obj, additionalHeaders = {}) { + return __awaiter(this, void 0, void 0, function* () { + const data = JSON.stringify(obj, null, 2); + additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson); + additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson); + const res = yield this.patch(requestUrl, data, additionalHeaders); + return this._processResponse(res, this.requestOptions); + }); + } + /** + * Makes a raw http request. + * All other methods such as get, post, patch, and request ultimately call this. + * Prefer get, del, post and patch + */ + request(verb, requestUrl, data, headers) { + return __awaiter(this, void 0, void 0, function* () { + if (this._disposed) { + throw new Error('Client has already been disposed.'); + } + const parsedUrl = new URL(requestUrl); + let info = this._prepareRequest(verb, parsedUrl, headers); + // Only perform retries on reads since writes may not be idempotent. + const maxTries = this._allowRetries && RetryableHttpVerbs.includes(verb) + ? this._maxRetries + 1 + : 1; + let numTries = 0; + let response; + do { + response = yield this.requestRaw(info, data); + // Check if it's an authentication challenge + if (response && + response.message && + response.message.statusCode === HttpCodes.Unauthorized) { + let authenticationHandler; + for (const handler of this.handlers) { + if (handler.canHandleAuthentication(response)) { + authenticationHandler = handler; + break; + } + } + if (authenticationHandler) { + return authenticationHandler.handleAuthentication(this, info, data); + } + else { + // We have received an unauthorized response but have no handlers to handle it. + // Let the response return to the caller. + return response; + } + } + let redirectsRemaining = this._maxRedirects; + while (response.message.statusCode && + HttpRedirectCodes.includes(response.message.statusCode) && + this._allowRedirects && + redirectsRemaining > 0) { + const redirectUrl = response.message.headers['location']; + if (!redirectUrl) { + // if there's no location to redirect to, we won't + break; + } + const parsedRedirectUrl = new URL(redirectUrl); + if (parsedUrl.protocol === 'https:' && + parsedUrl.protocol !== parsedRedirectUrl.protocol && + !this._allowRedirectDowngrade) { + throw new Error('Redirect from HTTPS to HTTP protocol. This downgrade is not allowed for security reasons. If you want to allow this behavior, set the allowRedirectDowngrade option to true.'); + } + // we need to finish reading the response before reassigning response + // which will leak the open socket. + yield response.readBody(); + // strip authorization header if redirected to a different hostname + if (parsedRedirectUrl.hostname !== parsedUrl.hostname) { + for (const header in headers) { + // header names are case insensitive + if (header.toLowerCase() === 'authorization') { + delete headers[header]; + } + } + } + // let's make the request with the new redirectUrl + info = this._prepareRequest(verb, parsedRedirectUrl, headers); + response = yield this.requestRaw(info, data); + redirectsRemaining--; + } + if (!response.message.statusCode || + !HttpResponseRetryCodes.includes(response.message.statusCode)) { + // If not a retry code, return immediately instead of retrying + return response; + } + numTries += 1; + if (numTries < maxTries) { + yield response.readBody(); + yield this._performExponentialBackoff(numTries); + } + } while (numTries < maxTries); + return response; + }); + } + /** + * Needs to be called if keepAlive is set to true in request options. + */ + dispose() { + if (this._agent) { + this._agent.destroy(); + } + this._disposed = true; + } + /** + * Raw request. + * @param info + * @param data + */ + requestRaw(info, data) { + return __awaiter(this, void 0, void 0, function* () { + return new Promise((resolve, reject) => { + function callbackForResult(err, res) { + if (err) { + reject(err); + } + else if (!res) { + // If `err` is not passed, then `res` must be passed. + reject(new Error('Unknown error')); + } + else { + resolve(res); + } + } + this.requestRawWithCallback(info, data, callbackForResult); + }); + }); + } + /** + * Raw request with callback. + * @param info + * @param data + * @param onResult + */ + requestRawWithCallback(info, data, onResult) { + if (typeof data === 'string') { + if (!info.options.headers) { + info.options.headers = {}; + } + info.options.headers['Content-Length'] = Buffer.byteLength(data, 'utf8'); + } + let callbackCalled = false; + function handleResult(err, res) { + if (!callbackCalled) { + callbackCalled = true; + onResult(err, res); + } + } + const req = info.httpModule.request(info.options, (msg) => { + const res = new HttpClientResponse(msg); + handleResult(undefined, res); + }); + let socket; + req.on('socket', sock => { + socket = sock; + }); + // If we ever get disconnected, we want the socket to timeout eventually + req.setTimeout(this._socketTimeout || 3 * 60000, () => { + if (socket) { + socket.end(); + } + handleResult(new Error(`Request timeout: ${info.options.path}`)); + }); + req.on('error', function (err) { + // err has statusCode property + // res should have headers + handleResult(err); + }); + if (data && typeof data === 'string') { + req.write(data, 'utf8'); + } + if (data && typeof data !== 'string') { + data.on('close', function () { + req.end(); + }); + data.pipe(req); + } + else { + req.end(); + } + } + /** + * Gets an http agent. This function is useful when you need an http agent that handles + * routing through a proxy server - depending upon the url and proxy environment variables. + * @param serverUrl The server URL where the request will be sent. For example, https://api.github.com + */ + getAgent(serverUrl) { + const parsedUrl = new URL(serverUrl); + return this._getAgent(parsedUrl); + } + getAgentDispatcher(serverUrl) { + const parsedUrl = new URL(serverUrl); + const proxyUrl = pm.getProxyUrl(parsedUrl); + const useProxy = proxyUrl && proxyUrl.hostname; + if (!useProxy) { + return; + } + return this._getProxyAgentDispatcher(parsedUrl, proxyUrl); + } + _prepareRequest(method, requestUrl, headers) { + const info = {}; + info.parsedUrl = requestUrl; + const usingSsl = info.parsedUrl.protocol === 'https:'; + info.httpModule = usingSsl ? https : http; + const defaultPort = usingSsl ? 443 : 80; + info.options = {}; + info.options.host = info.parsedUrl.hostname; + info.options.port = info.parsedUrl.port + ? parseInt(info.parsedUrl.port) + : defaultPort; + info.options.path = + (info.parsedUrl.pathname || '') + (info.parsedUrl.search || ''); + info.options.method = method; + info.options.headers = this._mergeHeaders(headers); + if (this.userAgent != null) { + info.options.headers['user-agent'] = this.userAgent; + } + info.options.agent = this._getAgent(info.parsedUrl); + // gives handlers an opportunity to participate + if (this.handlers) { + for (const handler of this.handlers) { + handler.prepareRequest(info.options); + } + } + return info; + } + _mergeHeaders(headers) { + if (this.requestOptions && this.requestOptions.headers) { + return Object.assign({}, lowercaseKeys(this.requestOptions.headers), lowercaseKeys(headers || {})); + } + return lowercaseKeys(headers || {}); + } + _getExistingOrDefaultHeader(additionalHeaders, header, _default) { + let clientHeader; + if (this.requestOptions && this.requestOptions.headers) { + clientHeader = lowercaseKeys(this.requestOptions.headers)[header]; + } + return additionalHeaders[header] || clientHeader || _default; + } + _getAgent(parsedUrl) { + let agent; + const proxyUrl = pm.getProxyUrl(parsedUrl); + const useProxy = proxyUrl && proxyUrl.hostname; + if (this._keepAlive && useProxy) { + agent = this._proxyAgent; + } + if (this._keepAlive && !useProxy) { + agent = this._agent; + } + // if agent is already assigned use that agent. + if (agent) { + return agent; + } + const usingSsl = parsedUrl.protocol === 'https:'; + let maxSockets = 100; + if (this.requestOptions) { + maxSockets = this.requestOptions.maxSockets || http.globalAgent.maxSockets; + } + // This is `useProxy` again, but we need to check `proxyURl` directly for TypeScripts's flow analysis. + if (proxyUrl && proxyUrl.hostname) { + const agentOptions = { + maxSockets, + keepAlive: this._keepAlive, + proxy: Object.assign(Object.assign({}, ((proxyUrl.username || proxyUrl.password) && { + proxyAuth: `${proxyUrl.username}:${proxyUrl.password}` + })), { host: proxyUrl.hostname, port: proxyUrl.port }) + }; + let tunnelAgent; + const overHttps = proxyUrl.protocol === 'https:'; + if (usingSsl) { + tunnelAgent = overHttps ? tunnel.httpsOverHttps : tunnel.httpsOverHttp; + } + else { + tunnelAgent = overHttps ? tunnel.httpOverHttps : tunnel.httpOverHttp; + } + agent = tunnelAgent(agentOptions); + this._proxyAgent = agent; + } + // if reusing agent across request and tunneling agent isn't assigned create a new agent + if (this._keepAlive && !agent) { + const options = { keepAlive: this._keepAlive, maxSockets }; + agent = usingSsl ? new https.Agent(options) : new http.Agent(options); + this._agent = agent; + } + // if not using private agent and tunnel agent isn't setup then use global agent + if (!agent) { + agent = usingSsl ? https.globalAgent : http.globalAgent; + } + if (usingSsl && this._ignoreSslError) { + // we don't want to set NODE_TLS_REJECT_UNAUTHORIZED=0 since that will affect request for entire process + // http.RequestOptions doesn't expose a way to modify RequestOptions.agent.options + // we have to cast it to any and change it directly + agent.options = Object.assign(agent.options || {}, { + rejectUnauthorized: false + }); + } + return agent; + } + _getProxyAgentDispatcher(parsedUrl, proxyUrl) { + let proxyAgent; + if (this._keepAlive) { + proxyAgent = this._proxyAgentDispatcher; + } + // if agent is already assigned use that agent. + if (proxyAgent) { + return proxyAgent; + } + const usingSsl = parsedUrl.protocol === 'https:'; + proxyAgent = new undici_1.ProxyAgent(Object.assign({ uri: proxyUrl.href, pipelining: !this._keepAlive ? 0 : 1 }, ((proxyUrl.username || proxyUrl.password) && { + token: `${proxyUrl.username}:${proxyUrl.password}` + }))); + this._proxyAgentDispatcher = proxyAgent; + if (usingSsl && this._ignoreSslError) { + // we don't want to set NODE_TLS_REJECT_UNAUTHORIZED=0 since that will affect request for entire process + // http.RequestOptions doesn't expose a way to modify RequestOptions.agent.options + // we have to cast it to any and change it directly + proxyAgent.options = Object.assign(proxyAgent.options.requestTls || {}, { + rejectUnauthorized: false + }); + } + return proxyAgent; + } + _performExponentialBackoff(retryNumber) { + return __awaiter(this, void 0, void 0, function* () { + retryNumber = Math.min(ExponentialBackoffCeiling, retryNumber); + const ms = ExponentialBackoffTimeSlice * Math.pow(2, retryNumber); + return new Promise(resolve => setTimeout(() => resolve(), ms)); + }); + } + _processResponse(res, options) { + return __awaiter(this, void 0, void 0, function* () { + return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { + const statusCode = res.message.statusCode || 0; + const response = { + statusCode, + result: null, + headers: {} + }; + // not found leads to null obj returned + if (statusCode === HttpCodes.NotFound) { + resolve(response); + } + // get the result from the body + function dateTimeDeserializer(key, value) { + if (typeof value === 'string') { + const a = new Date(value); + if (!isNaN(a.valueOf())) { + return a; + } + } + return value; + } + let obj; + let contents; + try { + contents = yield res.readBody(); + if (contents && contents.length > 0) { + if (options && options.deserializeDates) { + obj = JSON.parse(contents, dateTimeDeserializer); + } + else { + obj = JSON.parse(contents); + } + response.result = obj; + } + response.headers = res.message.headers; + } + catch (err) { + // Invalid resource (contents not json); leaving result obj null + } + // note that 3xx redirects are handled by the http layer. + if (statusCode > 299) { + let msg; + // if exception/error in body, attempt to get better error + if (obj && obj.message) { + msg = obj.message; + } + else if (contents && contents.length > 0) { + // it may be the case that the exception is in the body message as string + msg = contents; + } + else { + msg = `Failed request: (${statusCode})`; + } + const err = new HttpClientError(msg, statusCode); + err.result = response.result; + reject(err); + } + else { + resolve(response); + } + })); + }); + } +} +exports.HttpClient = HttpClient; +const lowercaseKeys = (obj) => Object.keys(obj).reduce((c, k) => ((c[k.toLowerCase()] = obj[k]), c), {}); +//# sourceMappingURL=index.js.map + +/***/ }), + +/***/ 9835: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.checkBypass = exports.getProxyUrl = void 0; +function getProxyUrl(reqUrl) { + const usingSsl = reqUrl.protocol === 'https:'; + if (checkBypass(reqUrl)) { + return undefined; + } + const proxyVar = (() => { + if (usingSsl) { + return process.env['https_proxy'] || process.env['HTTPS_PROXY']; + } + else { + return process.env['http_proxy'] || process.env['HTTP_PROXY']; + } + })(); + if (proxyVar) { + try { + return new URL(proxyVar); + } + catch (_a) { + if (!proxyVar.startsWith('http://') && !proxyVar.startsWith('https://')) + return new URL(`http://${proxyVar}`); + } + } + else { + return undefined; + } +} +exports.getProxyUrl = getProxyUrl; +function checkBypass(reqUrl) { + if (!reqUrl.hostname) { + return false; + } + const reqHost = reqUrl.hostname; + if (isLoopbackAddress(reqHost)) { + return true; + } + const noProxy = process.env['no_proxy'] || process.env['NO_PROXY'] || ''; + if (!noProxy) { + return false; + } + // Determine the request port + let reqPort; + if (reqUrl.port) { + reqPort = Number(reqUrl.port); + } + else if (reqUrl.protocol === 'http:') { + reqPort = 80; + } + else if (reqUrl.protocol === 'https:') { + reqPort = 443; + } + // Format the request hostname and hostname with port + const upperReqHosts = [reqUrl.hostname.toUpperCase()]; + if (typeof reqPort === 'number') { + upperReqHosts.push(`${upperReqHosts[0]}:${reqPort}`); + } + // Compare request host against noproxy + for (const upperNoProxyItem of noProxy + .split(',') + .map(x => x.trim().toUpperCase()) + .filter(x => x)) { + if (upperNoProxyItem === '*' || + upperReqHosts.some(x => x === upperNoProxyItem || + x.endsWith(`.${upperNoProxyItem}`) || + (upperNoProxyItem.startsWith('.') && + x.endsWith(`${upperNoProxyItem}`)))) { + return true; + } + } + return false; +} +exports.checkBypass = checkBypass; +function isLoopbackAddress(host) { + const hostLower = host.toLowerCase(); + return (hostLower === 'localhost' || + hostLower.startsWith('127.') || + hostLower.startsWith('[::1]') || + hostLower.startsWith('[0:0:0:0:0:0:0:1]')); +} +//# sourceMappingURL=proxy.js.map + +/***/ }), + +/***/ 2856: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const WritableStream = (__nccwpck_require__(4492).Writable) +const inherits = (__nccwpck_require__(7261).inherits) + +const StreamSearch = __nccwpck_require__(8534) + +const PartStream = __nccwpck_require__(8710) +const HeaderParser = __nccwpck_require__(333) + +const DASH = 45 +const B_ONEDASH = Buffer.from('-') +const B_CRLF = Buffer.from('\r\n') +const EMPTY_FN = function () {} + +function Dicer (cfg) { + if (!(this instanceof Dicer)) { return new Dicer(cfg) } + WritableStream.call(this, cfg) + + if (!cfg || (!cfg.headerFirst && typeof cfg.boundary !== 'string')) { throw new TypeError('Boundary required') } + + if (typeof cfg.boundary === 'string') { this.setBoundary(cfg.boundary) } else { this._bparser = undefined } + + this._headerFirst = cfg.headerFirst + + this._dashes = 0 + this._parts = 0 + this._finished = false + this._realFinish = false + this._isPreamble = true + this._justMatched = false + this._firstWrite = true + this._inHeader = true + this._part = undefined + this._cb = undefined + this._ignoreData = false + this._partOpts = { highWaterMark: cfg.partHwm } + this._pause = false + + const self = this + this._hparser = new HeaderParser(cfg) + this._hparser.on('header', function (header) { + self._inHeader = false + self._part.emit('header', header) + }) +} +inherits(Dicer, WritableStream) + +Dicer.prototype.emit = function (ev) { + if (ev === 'finish' && !this._realFinish) { + if (!this._finished) { + const self = this + process.nextTick(function () { + self.emit('error', new Error('Unexpected end of multipart data')) + if (self._part && !self._ignoreData) { + const type = (self._isPreamble ? 'Preamble' : 'Part') + self._part.emit('error', new Error(type + ' terminated early due to unexpected end of multipart data')) + self._part.push(null) + process.nextTick(function () { + self._realFinish = true + self.emit('finish') + self._realFinish = false + }) + return + } + self._realFinish = true + self.emit('finish') + self._realFinish = false + }) + } + } else { WritableStream.prototype.emit.apply(this, arguments) } +} + +Dicer.prototype._write = function (data, encoding, cb) { + // ignore unexpected data (e.g. extra trailer data after finished) + if (!this._hparser && !this._bparser) { return cb() } + + if (this._headerFirst && this._isPreamble) { + if (!this._part) { + this._part = new PartStream(this._partOpts) + if (this._events.preamble) { this.emit('preamble', this._part) } else { this._ignore() } + } + const r = this._hparser.push(data) + if (!this._inHeader && r !== undefined && r < data.length) { data = data.slice(r) } else { return cb() } + } + + // allows for "easier" testing + if (this._firstWrite) { + this._bparser.push(B_CRLF) + this._firstWrite = false + } + + this._bparser.push(data) + + if (this._pause) { this._cb = cb } else { cb() } +} + +Dicer.prototype.reset = function () { + this._part = undefined + this._bparser = undefined + this._hparser = undefined +} + +Dicer.prototype.setBoundary = function (boundary) { + const self = this + this._bparser = new StreamSearch('\r\n--' + boundary) + this._bparser.on('info', function (isMatch, data, start, end) { + self._oninfo(isMatch, data, start, end) + }) +} + +Dicer.prototype._ignore = function () { + if (this._part && !this._ignoreData) { + this._ignoreData = true + this._part.on('error', EMPTY_FN) + // we must perform some kind of read on the stream even though we are + // ignoring the data, otherwise node's Readable stream will not emit 'end' + // after pushing null to the stream + this._part.resume() + } +} + +Dicer.prototype._oninfo = function (isMatch, data, start, end) { + let buf; const self = this; let i = 0; let r; let shouldWriteMore = true + + if (!this._part && this._justMatched && data) { + while (this._dashes < 2 && (start + i) < end) { + if (data[start + i] === DASH) { + ++i + ++this._dashes + } else { + if (this._dashes) { buf = B_ONEDASH } + this._dashes = 0 + break + } + } + if (this._dashes === 2) { + if ((start + i) < end && this._events.trailer) { this.emit('trailer', data.slice(start + i, end)) } + this.reset() + this._finished = true + // no more parts will be added + if (self._parts === 0) { + self._realFinish = true + self.emit('finish') + self._realFinish = false + } + } + if (this._dashes) { return } + } + if (this._justMatched) { this._justMatched = false } + if (!this._part) { + this._part = new PartStream(this._partOpts) + this._part._read = function (n) { + self._unpause() + } + if (this._isPreamble && this._events.preamble) { this.emit('preamble', this._part) } else if (this._isPreamble !== true && this._events.part) { this.emit('part', this._part) } else { this._ignore() } + if (!this._isPreamble) { this._inHeader = true } + } + if (data && start < end && !this._ignoreData) { + if (this._isPreamble || !this._inHeader) { + if (buf) { shouldWriteMore = this._part.push(buf) } + shouldWriteMore = this._part.push(data.slice(start, end)) + if (!shouldWriteMore) { this._pause = true } + } else if (!this._isPreamble && this._inHeader) { + if (buf) { this._hparser.push(buf) } + r = this._hparser.push(data.slice(start, end)) + if (!this._inHeader && r !== undefined && r < end) { this._oninfo(false, data, start + r, end) } + } + } + if (isMatch) { + this._hparser.reset() + if (this._isPreamble) { this._isPreamble = false } else { + if (start !== end) { + ++this._parts + this._part.on('end', function () { + if (--self._parts === 0) { + if (self._finished) { + self._realFinish = true + self.emit('finish') + self._realFinish = false + } else { + self._unpause() + } + } + }) + } + } + this._part.push(null) + this._part = undefined + this._ignoreData = false + this._justMatched = true + this._dashes = 0 + } +} + +Dicer.prototype._unpause = function () { + if (!this._pause) { return } + + this._pause = false + if (this._cb) { + const cb = this._cb + this._cb = undefined + cb() + } +} + +module.exports = Dicer + + +/***/ }), + +/***/ 333: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const EventEmitter = (__nccwpck_require__(5673).EventEmitter) +const inherits = (__nccwpck_require__(7261).inherits) +const getLimit = __nccwpck_require__(9692) + +const StreamSearch = __nccwpck_require__(8534) + +const B_DCRLF = Buffer.from('\r\n\r\n') +const RE_CRLF = /\r\n/g +const RE_HDR = /^([^:]+):[ \t]?([\x00-\xFF]+)?$/ // eslint-disable-line no-control-regex + +function HeaderParser (cfg) { + EventEmitter.call(this) + + cfg = cfg || {} + const self = this + this.nread = 0 + this.maxed = false + this.npairs = 0 + this.maxHeaderPairs = getLimit(cfg, 'maxHeaderPairs', 2000) + this.maxHeaderSize = getLimit(cfg, 'maxHeaderSize', 80 * 1024) + this.buffer = '' + this.header = {} + this.finished = false + this.ss = new StreamSearch(B_DCRLF) + this.ss.on('info', function (isMatch, data, start, end) { + if (data && !self.maxed) { + if (self.nread + end - start >= self.maxHeaderSize) { + end = self.maxHeaderSize - self.nread + start + self.nread = self.maxHeaderSize + self.maxed = true + } else { self.nread += (end - start) } + + self.buffer += data.toString('binary', start, end) + } + if (isMatch) { self._finish() } + }) +} +inherits(HeaderParser, EventEmitter) + +HeaderParser.prototype.push = function (data) { + const r = this.ss.push(data) + if (this.finished) { return r } +} + +HeaderParser.prototype.reset = function () { + this.finished = false + this.buffer = '' + this.header = {} + this.ss.reset() +} + +HeaderParser.prototype._finish = function () { + if (this.buffer) { this._parseHeader() } + this.ss.matches = this.ss.maxMatches + const header = this.header + this.header = {} + this.buffer = '' + this.finished = true + this.nread = this.npairs = 0 + this.maxed = false + this.emit('header', header) +} + +HeaderParser.prototype._parseHeader = function () { + if (this.npairs === this.maxHeaderPairs) { return } + + const lines = this.buffer.split(RE_CRLF) + const len = lines.length + let m, h + + for (var i = 0; i < len; ++i) { // eslint-disable-line no-var + if (lines[i].length === 0) { continue } + if (lines[i][0] === '\t' || lines[i][0] === ' ') { + // folded header content + // RFC2822 says to just remove the CRLF and not the whitespace following + // it, so we follow the RFC and include the leading whitespace ... + if (h) { + this.header[h][this.header[h].length - 1] += lines[i] + continue + } + } + + const posColon = lines[i].indexOf(':') + if ( + posColon === -1 || + posColon === 0 + ) { + return + } + m = RE_HDR.exec(lines[i]) + h = m[1].toLowerCase() + this.header[h] = this.header[h] || [] + this.header[h].push((m[2] || '')) + if (++this.npairs === this.maxHeaderPairs) { break } + } +} + +module.exports = HeaderParser + + +/***/ }), + +/***/ 8710: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const inherits = (__nccwpck_require__(7261).inherits) +const ReadableStream = (__nccwpck_require__(4492).Readable) + +function PartStream (opts) { + ReadableStream.call(this, opts) +} +inherits(PartStream, ReadableStream) + +PartStream.prototype._read = function (n) {} + +module.exports = PartStream + + +/***/ }), + +/***/ 8534: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +/** + * Copyright Brian White. All rights reserved. + * + * @see https://github.com/mscdex/streamsearch + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + * + * Based heavily on the Streaming Boyer-Moore-Horspool C++ implementation + * by Hongli Lai at: https://github.com/FooBarWidget/boyer-moore-horspool + */ +const EventEmitter = (__nccwpck_require__(5673).EventEmitter) +const inherits = (__nccwpck_require__(7261).inherits) + +function SBMH (needle) { + if (typeof needle === 'string') { + needle = Buffer.from(needle) + } + + if (!Buffer.isBuffer(needle)) { + throw new TypeError('The needle has to be a String or a Buffer.') + } + + const needleLength = needle.length + + if (needleLength === 0) { + throw new Error('The needle cannot be an empty String/Buffer.') + } + + if (needleLength > 256) { + throw new Error('The needle cannot have a length bigger than 256.') + } + + this.maxMatches = Infinity + this.matches = 0 + + this._occ = new Array(256) + .fill(needleLength) // Initialize occurrence table. + this._lookbehind_size = 0 + this._needle = needle + this._bufpos = 0 + + this._lookbehind = Buffer.alloc(needleLength) + + // Populate occurrence table with analysis of the needle, + // ignoring last letter. + for (var i = 0; i < needleLength - 1; ++i) { // eslint-disable-line no-var + this._occ[needle[i]] = needleLength - 1 - i + } +} +inherits(SBMH, EventEmitter) + +SBMH.prototype.reset = function () { + this._lookbehind_size = 0 + this.matches = 0 + this._bufpos = 0 +} + +SBMH.prototype.push = function (chunk, pos) { + if (!Buffer.isBuffer(chunk)) { + chunk = Buffer.from(chunk, 'binary') + } + const chlen = chunk.length + this._bufpos = pos || 0 + let r + while (r !== chlen && this.matches < this.maxMatches) { r = this._sbmh_feed(chunk) } + return r +} + +SBMH.prototype._sbmh_feed = function (data) { + const len = data.length + const needle = this._needle + const needleLength = needle.length + const lastNeedleChar = needle[needleLength - 1] + + // Positive: points to a position in `data` + // pos == 3 points to data[3] + // Negative: points to a position in the lookbehind buffer + // pos == -2 points to lookbehind[lookbehind_size - 2] + let pos = -this._lookbehind_size + let ch + + if (pos < 0) { + // Lookbehind buffer is not empty. Perform Boyer-Moore-Horspool + // search with character lookup code that considers both the + // lookbehind buffer and the current round's haystack data. + // + // Loop until + // there is a match. + // or until + // we've moved past the position that requires the + // lookbehind buffer. In this case we switch to the + // optimized loop. + // or until + // the character to look at lies outside the haystack. + while (pos < 0 && pos <= len - needleLength) { + ch = this._sbmh_lookup_char(data, pos + needleLength - 1) + + if ( + ch === lastNeedleChar && + this._sbmh_memcmp(data, pos, needleLength - 1) + ) { + this._lookbehind_size = 0 + ++this.matches + this.emit('info', true) + + return (this._bufpos = pos + needleLength) + } + pos += this._occ[ch] + } + + // No match. + + if (pos < 0) { + // There's too few data for Boyer-Moore-Horspool to run, + // so let's use a different algorithm to skip as much as + // we can. + // Forward pos until + // the trailing part of lookbehind + data + // looks like the beginning of the needle + // or until + // pos == 0 + while (pos < 0 && !this._sbmh_memcmp(data, pos, len - pos)) { ++pos } + } + + if (pos >= 0) { + // Discard lookbehind buffer. + this.emit('info', false, this._lookbehind, 0, this._lookbehind_size) + this._lookbehind_size = 0 + } else { + // Cut off part of the lookbehind buffer that has + // been processed and append the entire haystack + // into it. + const bytesToCutOff = this._lookbehind_size + pos + if (bytesToCutOff > 0) { + // The cut off data is guaranteed not to contain the needle. + this.emit('info', false, this._lookbehind, 0, bytesToCutOff) + } + + this._lookbehind.copy(this._lookbehind, 0, bytesToCutOff, + this._lookbehind_size - bytesToCutOff) + this._lookbehind_size -= bytesToCutOff + + data.copy(this._lookbehind, this._lookbehind_size) + this._lookbehind_size += len + + this._bufpos = len + return len + } + } + + pos += (pos >= 0) * this._bufpos + + // Lookbehind buffer is now empty. We only need to check if the + // needle is in the haystack. + if (data.indexOf(needle, pos) !== -1) { + pos = data.indexOf(needle, pos) + ++this.matches + if (pos > 0) { this.emit('info', true, data, this._bufpos, pos) } else { this.emit('info', true) } + + return (this._bufpos = pos + needleLength) + } else { + pos = len - needleLength + } + + // There was no match. If there's trailing haystack data that we cannot + // match yet using the Boyer-Moore-Horspool algorithm (because the trailing + // data is less than the needle size) then match using a modified + // algorithm that starts matching from the beginning instead of the end. + // Whatever trailing data is left after running this algorithm is added to + // the lookbehind buffer. + while ( + pos < len && + ( + data[pos] !== needle[0] || + ( + (Buffer.compare( + data.subarray(pos, pos + len - pos), + needle.subarray(0, len - pos) + ) !== 0) + ) + ) + ) { + ++pos + } + if (pos < len) { + data.copy(this._lookbehind, 0, pos, pos + (len - pos)) + this._lookbehind_size = len - pos + } + + // Everything until pos is guaranteed not to contain needle data. + if (pos > 0) { this.emit('info', false, data, this._bufpos, pos < len ? pos : len) } + + this._bufpos = len + return len +} + +SBMH.prototype._sbmh_lookup_char = function (data, pos) { + return (pos < 0) + ? this._lookbehind[this._lookbehind_size + pos] + : data[pos] +} + +SBMH.prototype._sbmh_memcmp = function (data, pos, len) { + for (var i = 0; i < len; ++i) { // eslint-disable-line no-var + if (this._sbmh_lookup_char(data, pos + i) !== this._needle[i]) { return false } + } + return true +} + +module.exports = SBMH + + +/***/ }), + +/***/ 3438: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const WritableStream = (__nccwpck_require__(4492).Writable) +const { inherits } = __nccwpck_require__(7261) +const Dicer = __nccwpck_require__(2856) + +const MultipartParser = __nccwpck_require__(415) +const UrlencodedParser = __nccwpck_require__(6780) +const parseParams = __nccwpck_require__(4426) + +function Busboy (opts) { + if (!(this instanceof Busboy)) { return new Busboy(opts) } + + if (typeof opts !== 'object') { + throw new TypeError('Busboy expected an options-Object.') + } + if (typeof opts.headers !== 'object') { + throw new TypeError('Busboy expected an options-Object with headers-attribute.') + } + if (typeof opts.headers['content-type'] !== 'string') { + throw new TypeError('Missing Content-Type-header.') + } + + const { + headers, + ...streamOptions + } = opts + + this.opts = { + autoDestroy: false, + ...streamOptions + } + WritableStream.call(this, this.opts) + + this._done = false + this._parser = this.getParserByHeaders(headers) + this._finished = false +} +inherits(Busboy, WritableStream) + +Busboy.prototype.emit = function (ev) { + if (ev === 'finish') { + if (!this._done) { + this._parser?.end() + return + } else if (this._finished) { + return + } + this._finished = true + } + WritableStream.prototype.emit.apply(this, arguments) +} + +Busboy.prototype.getParserByHeaders = function (headers) { + const parsed = parseParams(headers['content-type']) + + const cfg = { + defCharset: this.opts.defCharset, + fileHwm: this.opts.fileHwm, + headers, + highWaterMark: this.opts.highWaterMark, + isPartAFile: this.opts.isPartAFile, + limits: this.opts.limits, + parsedConType: parsed, + preservePath: this.opts.preservePath + } + + if (MultipartParser.detect.test(parsed[0])) { + return new MultipartParser(this, cfg) + } + if (UrlencodedParser.detect.test(parsed[0])) { + return new UrlencodedParser(this, cfg) + } + throw new Error('Unsupported Content-Type.') +} + +Busboy.prototype._write = function (chunk, encoding, cb) { + this._parser.write(chunk, cb) +} + +module.exports = Busboy +module.exports["default"] = Busboy +module.exports.Busboy = Busboy + +module.exports.Dicer = Dicer + + +/***/ }), + +/***/ 415: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +// TODO: +// * support 1 nested multipart level +// (see second multipart example here: +// http://www.w3.org/TR/html401/interact/forms.html#didx-multipartform-data) +// * support limits.fieldNameSize +// -- this will require modifications to utils.parseParams + +const { Readable } = __nccwpck_require__(4492) +const { inherits } = __nccwpck_require__(7261) + +const Dicer = __nccwpck_require__(2856) + +const parseParams = __nccwpck_require__(4426) +const decodeText = __nccwpck_require__(9136) +const basename = __nccwpck_require__(496) +const getLimit = __nccwpck_require__(9692) + +const RE_BOUNDARY = /^boundary$/i +const RE_FIELD = /^form-data$/i +const RE_CHARSET = /^charset$/i +const RE_FILENAME = /^filename$/i +const RE_NAME = /^name$/i + +Multipart.detect = /^multipart\/form-data/i +function Multipart (boy, cfg) { + let i + let len + const self = this + let boundary + const limits = cfg.limits + const isPartAFile = cfg.isPartAFile || ((fieldName, contentType, fileName) => (contentType === 'application/octet-stream' || fileName !== undefined)) + const parsedConType = cfg.parsedConType || [] + const defCharset = cfg.defCharset || 'utf8' + const preservePath = cfg.preservePath + const fileOpts = { highWaterMark: cfg.fileHwm } + + for (i = 0, len = parsedConType.length; i < len; ++i) { + if (Array.isArray(parsedConType[i]) && + RE_BOUNDARY.test(parsedConType[i][0])) { + boundary = parsedConType[i][1] + break + } + } + + function checkFinished () { + if (nends === 0 && finished && !boy._done) { + finished = false + self.end() + } + } + + if (typeof boundary !== 'string') { throw new Error('Multipart: Boundary not found') } + + const fieldSizeLimit = getLimit(limits, 'fieldSize', 1 * 1024 * 1024) + const fileSizeLimit = getLimit(limits, 'fileSize', Infinity) + const filesLimit = getLimit(limits, 'files', Infinity) + const fieldsLimit = getLimit(limits, 'fields', Infinity) + const partsLimit = getLimit(limits, 'parts', Infinity) + const headerPairsLimit = getLimit(limits, 'headerPairs', 2000) + const headerSizeLimit = getLimit(limits, 'headerSize', 80 * 1024) + + let nfiles = 0 + let nfields = 0 + let nends = 0 + let curFile + let curField + let finished = false + + this._needDrain = false + this._pause = false + this._cb = undefined + this._nparts = 0 + this._boy = boy + + const parserCfg = { + boundary, + maxHeaderPairs: headerPairsLimit, + maxHeaderSize: headerSizeLimit, + partHwm: fileOpts.highWaterMark, + highWaterMark: cfg.highWaterMark + } + + this.parser = new Dicer(parserCfg) + this.parser.on('drain', function () { + self._needDrain = false + if (self._cb && !self._pause) { + const cb = self._cb + self._cb = undefined + cb() + } + }).on('part', function onPart (part) { + if (++self._nparts > partsLimit) { + self.parser.removeListener('part', onPart) + self.parser.on('part', skipPart) + boy.hitPartsLimit = true + boy.emit('partsLimit') + return skipPart(part) + } + + // hack because streams2 _always_ doesn't emit 'end' until nextTick, so let + // us emit 'end' early since we know the part has ended if we are already + // seeing the next part + if (curField) { + const field = curField + field.emit('end') + field.removeAllListeners('end') + } + + part.on('header', function (header) { + let contype + let fieldname + let parsed + let charset + let encoding + let filename + let nsize = 0 + + if (header['content-type']) { + parsed = parseParams(header['content-type'][0]) + if (parsed[0]) { + contype = parsed[0].toLowerCase() + for (i = 0, len = parsed.length; i < len; ++i) { + if (RE_CHARSET.test(parsed[i][0])) { + charset = parsed[i][1].toLowerCase() + break + } + } + } + } + + if (contype === undefined) { contype = 'text/plain' } + if (charset === undefined) { charset = defCharset } + + if (header['content-disposition']) { + parsed = parseParams(header['content-disposition'][0]) + if (!RE_FIELD.test(parsed[0])) { return skipPart(part) } + for (i = 0, len = parsed.length; i < len; ++i) { + if (RE_NAME.test(parsed[i][0])) { + fieldname = parsed[i][1] + } else if (RE_FILENAME.test(parsed[i][0])) { + filename = parsed[i][1] + if (!preservePath) { filename = basename(filename) } + } + } + } else { return skipPart(part) } + + if (header['content-transfer-encoding']) { encoding = header['content-transfer-encoding'][0].toLowerCase() } else { encoding = '7bit' } + + let onData, + onEnd + + if (isPartAFile(fieldname, contype, filename)) { + // file/binary field + if (nfiles === filesLimit) { + if (!boy.hitFilesLimit) { + boy.hitFilesLimit = true + boy.emit('filesLimit') + } + return skipPart(part) + } + + ++nfiles + + if (!boy._events.file) { + self.parser._ignore() + return + } + + ++nends + const file = new FileStream(fileOpts) + curFile = file + file.on('end', function () { + --nends + self._pause = false + checkFinished() + if (self._cb && !self._needDrain) { + const cb = self._cb + self._cb = undefined + cb() + } + }) + file._read = function (n) { + if (!self._pause) { return } + self._pause = false + if (self._cb && !self._needDrain) { + const cb = self._cb + self._cb = undefined + cb() + } + } + boy.emit('file', fieldname, file, filename, encoding, contype) + + onData = function (data) { + if ((nsize += data.length) > fileSizeLimit) { + const extralen = fileSizeLimit - nsize + data.length + if (extralen > 0) { file.push(data.slice(0, extralen)) } + file.truncated = true + file.bytesRead = fileSizeLimit + part.removeAllListeners('data') + file.emit('limit') + return + } else if (!file.push(data)) { self._pause = true } + + file.bytesRead = nsize + } + + onEnd = function () { + curFile = undefined + file.push(null) + } + } else { + // non-file field + if (nfields === fieldsLimit) { + if (!boy.hitFieldsLimit) { + boy.hitFieldsLimit = true + boy.emit('fieldsLimit') + } + return skipPart(part) + } + + ++nfields + ++nends + let buffer = '' + let truncated = false + curField = part + + onData = function (data) { + if ((nsize += data.length) > fieldSizeLimit) { + const extralen = (fieldSizeLimit - (nsize - data.length)) + buffer += data.toString('binary', 0, extralen) + truncated = true + part.removeAllListeners('data') + } else { buffer += data.toString('binary') } + } + + onEnd = function () { + curField = undefined + if (buffer.length) { buffer = decodeText(buffer, 'binary', charset) } + boy.emit('field', fieldname, buffer, false, truncated, encoding, contype) + --nends + checkFinished() + } + } + + /* As of node@2efe4ab761666 (v0.10.29+/v0.11.14+), busboy had become + broken. Streams2/streams3 is a huge black box of confusion, but + somehow overriding the sync state seems to fix things again (and still + seems to work for previous node versions). + */ + part._readableState.sync = false + + part.on('data', onData) + part.on('end', onEnd) + }).on('error', function (err) { + if (curFile) { curFile.emit('error', err) } + }) + }).on('error', function (err) { + boy.emit('error', err) + }).on('finish', function () { + finished = true + checkFinished() + }) +} + +Multipart.prototype.write = function (chunk, cb) { + const r = this.parser.write(chunk) + if (r && !this._pause) { + cb() + } else { + this._needDrain = !r + this._cb = cb + } +} + +Multipart.prototype.end = function () { + const self = this + + if (self.parser.writable) { + self.parser.end() + } else if (!self._boy._done) { + process.nextTick(function () { + self._boy._done = true + self._boy.emit('finish') + }) + } +} + +function skipPart (part) { + part.resume() +} + +function FileStream (opts) { + Readable.call(this, opts) + + this.bytesRead = 0 + + this.truncated = false +} + +inherits(FileStream, Readable) + +FileStream.prototype._read = function (n) {} + +module.exports = Multipart + + +/***/ }), + +/***/ 6780: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const Decoder = __nccwpck_require__(9730) +const decodeText = __nccwpck_require__(9136) +const getLimit = __nccwpck_require__(9692) + +const RE_CHARSET = /^charset$/i + +UrlEncoded.detect = /^application\/x-www-form-urlencoded/i +function UrlEncoded (boy, cfg) { + const limits = cfg.limits + const parsedConType = cfg.parsedConType + this.boy = boy + + this.fieldSizeLimit = getLimit(limits, 'fieldSize', 1 * 1024 * 1024) + this.fieldNameSizeLimit = getLimit(limits, 'fieldNameSize', 100) + this.fieldsLimit = getLimit(limits, 'fields', Infinity) + + let charset + for (var i = 0, len = parsedConType.length; i < len; ++i) { // eslint-disable-line no-var + if (Array.isArray(parsedConType[i]) && + RE_CHARSET.test(parsedConType[i][0])) { + charset = parsedConType[i][1].toLowerCase() + break + } + } + + if (charset === undefined) { charset = cfg.defCharset || 'utf8' } + + this.decoder = new Decoder() + this.charset = charset + this._fields = 0 + this._state = 'key' + this._checkingBytes = true + this._bytesKey = 0 + this._bytesVal = 0 + this._key = '' + this._val = '' + this._keyTrunc = false + this._valTrunc = false + this._hitLimit = false +} + +UrlEncoded.prototype.write = function (data, cb) { + if (this._fields === this.fieldsLimit) { + if (!this.boy.hitFieldsLimit) { + this.boy.hitFieldsLimit = true + this.boy.emit('fieldsLimit') + } + return cb() + } + + let idxeq; let idxamp; let i; let p = 0; const len = data.length + + while (p < len) { + if (this._state === 'key') { + idxeq = idxamp = undefined + for (i = p; i < len; ++i) { + if (!this._checkingBytes) { ++p } + if (data[i] === 0x3D/* = */) { + idxeq = i + break + } else if (data[i] === 0x26/* & */) { + idxamp = i + break + } + if (this._checkingBytes && this._bytesKey === this.fieldNameSizeLimit) { + this._hitLimit = true + break + } else if (this._checkingBytes) { ++this._bytesKey } + } + + if (idxeq !== undefined) { + // key with assignment + if (idxeq > p) { this._key += this.decoder.write(data.toString('binary', p, idxeq)) } + this._state = 'val' + + this._hitLimit = false + this._checkingBytes = true + this._val = '' + this._bytesVal = 0 + this._valTrunc = false + this.decoder.reset() + + p = idxeq + 1 + } else if (idxamp !== undefined) { + // key with no assignment + ++this._fields + let key; const keyTrunc = this._keyTrunc + if (idxamp > p) { key = (this._key += this.decoder.write(data.toString('binary', p, idxamp))) } else { key = this._key } + + this._hitLimit = false + this._checkingBytes = true + this._key = '' + this._bytesKey = 0 + this._keyTrunc = false + this.decoder.reset() + + if (key.length) { + this.boy.emit('field', decodeText(key, 'binary', this.charset), + '', + keyTrunc, + false) + } + + p = idxamp + 1 + if (this._fields === this.fieldsLimit) { return cb() } + } else if (this._hitLimit) { + // we may not have hit the actual limit if there are encoded bytes... + if (i > p) { this._key += this.decoder.write(data.toString('binary', p, i)) } + p = i + if ((this._bytesKey = this._key.length) === this.fieldNameSizeLimit) { + // yep, we actually did hit the limit + this._checkingBytes = false + this._keyTrunc = true + } + } else { + if (p < len) { this._key += this.decoder.write(data.toString('binary', p)) } + p = len + } + } else { + idxamp = undefined + for (i = p; i < len; ++i) { + if (!this._checkingBytes) { ++p } + if (data[i] === 0x26/* & */) { + idxamp = i + break + } + if (this._checkingBytes && this._bytesVal === this.fieldSizeLimit) { + this._hitLimit = true + break + } else if (this._checkingBytes) { ++this._bytesVal } + } + + if (idxamp !== undefined) { + ++this._fields + if (idxamp > p) { this._val += this.decoder.write(data.toString('binary', p, idxamp)) } + this.boy.emit('field', decodeText(this._key, 'binary', this.charset), + decodeText(this._val, 'binary', this.charset), + this._keyTrunc, + this._valTrunc) + this._state = 'key' + + this._hitLimit = false + this._checkingBytes = true + this._key = '' + this._bytesKey = 0 + this._keyTrunc = false + this.decoder.reset() + + p = idxamp + 1 + if (this._fields === this.fieldsLimit) { return cb() } + } else if (this._hitLimit) { + // we may not have hit the actual limit if there are encoded bytes... + if (i > p) { this._val += this.decoder.write(data.toString('binary', p, i)) } + p = i + if ((this._val === '' && this.fieldSizeLimit === 0) || + (this._bytesVal = this._val.length) === this.fieldSizeLimit) { + // yep, we actually did hit the limit + this._checkingBytes = false + this._valTrunc = true + } + } else { + if (p < len) { this._val += this.decoder.write(data.toString('binary', p)) } + p = len + } + } + } + cb() +} + +UrlEncoded.prototype.end = function () { + if (this.boy._done) { return } + + if (this._state === 'key' && this._key.length > 0) { + this.boy.emit('field', decodeText(this._key, 'binary', this.charset), + '', + this._keyTrunc, + false) + } else if (this._state === 'val') { + this.boy.emit('field', decodeText(this._key, 'binary', this.charset), + decodeText(this._val, 'binary', this.charset), + this._keyTrunc, + this._valTrunc) + } + this.boy._done = true + this.boy.emit('finish') +} + +module.exports = UrlEncoded + + +/***/ }), + +/***/ 9730: +/***/ ((module) => { + +"use strict"; + + +const RE_PLUS = /\+/g + +const HEX = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +] + +function Decoder () { + this.buffer = undefined +} +Decoder.prototype.write = function (str) { + // Replace '+' with ' ' before decoding + str = str.replace(RE_PLUS, ' ') + let res = '' + let i = 0; let p = 0; const len = str.length + for (; i < len; ++i) { + if (this.buffer !== undefined) { + if (!HEX[str.charCodeAt(i)]) { + res += '%' + this.buffer + this.buffer = undefined + --i // retry character + } else { + this.buffer += str[i] + ++p + if (this.buffer.length === 2) { + res += String.fromCharCode(parseInt(this.buffer, 16)) + this.buffer = undefined + } + } + } else if (str[i] === '%') { + if (i > p) { + res += str.substring(p, i) + p = i + } + this.buffer = '' + ++p + } + } + if (p < len && this.buffer === undefined) { res += str.substring(p) } + return res +} +Decoder.prototype.reset = function () { + this.buffer = undefined +} + +module.exports = Decoder + + +/***/ }), + +/***/ 496: +/***/ ((module) => { + +"use strict"; + + +module.exports = function basename (path) { + if (typeof path !== 'string') { return '' } + for (var i = path.length - 1; i >= 0; --i) { // eslint-disable-line no-var + switch (path.charCodeAt(i)) { + case 0x2F: // '/' + case 0x5C: // '\' + path = path.slice(i + 1) + return (path === '..' || path === '.' ? '' : path) + } + } + return (path === '..' || path === '.' ? '' : path) +} + + +/***/ }), + +/***/ 9136: +/***/ ((module) => { + +"use strict"; + + +// Node has always utf-8 +const utf8Decoder = new TextDecoder('utf-8') +const textDecoders = new Map([ + ['utf-8', utf8Decoder], + ['utf8', utf8Decoder] +]) + +function decodeText (text, textEncoding, destEncoding) { + if (text) { + if (textDecoders.has(destEncoding)) { + try { + return textDecoders.get(destEncoding).decode(Buffer.from(text, textEncoding)) + } catch (e) { } + } else { + try { + textDecoders.set(destEncoding, new TextDecoder(destEncoding)) + return textDecoders.get(destEncoding).decode(Buffer.from(text, textEncoding)) + } catch (e) { } + } + } + return text +} + +module.exports = decodeText + + +/***/ }), + +/***/ 9692: +/***/ ((module) => { + +"use strict"; + + +module.exports = function getLimit (limits, name, defaultLimit) { + if ( + !limits || + limits[name] === undefined || + limits[name] === null + ) { return defaultLimit } + + if ( + typeof limits[name] !== 'number' || + isNaN(limits[name]) + ) { throw new TypeError('Limit ' + name + ' is not a valid number') } + + return limits[name] +} + + +/***/ }), + +/***/ 4426: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const decodeText = __nccwpck_require__(9136) + +const RE_ENCODED = /%([a-fA-F0-9]{2})/g + +function encodedReplacer (match, byte) { + return String.fromCharCode(parseInt(byte, 16)) +} + +function parseParams (str) { + const res = [] + let state = 'key' + let charset = '' + let inquote = false + let escaping = false + let p = 0 + let tmp = '' + + for (var i = 0, len = str.length; i < len; ++i) { // eslint-disable-line no-var + const char = str[i] + if (char === '\\' && inquote) { + if (escaping) { escaping = false } else { + escaping = true + continue + } + } else if (char === '"') { + if (!escaping) { + if (inquote) { + inquote = false + state = 'key' + } else { inquote = true } + continue + } else { escaping = false } + } else { + if (escaping && inquote) { tmp += '\\' } + escaping = false + if ((state === 'charset' || state === 'lang') && char === "'") { + if (state === 'charset') { + state = 'lang' + charset = tmp.substring(1) + } else { state = 'value' } + tmp = '' + continue + } else if (state === 'key' && + (char === '*' || char === '=') && + res.length) { + if (char === '*') { state = 'charset' } else { state = 'value' } + res[p] = [tmp, undefined] + tmp = '' + continue + } else if (!inquote && char === ';') { + state = 'key' + if (charset) { + if (tmp.length) { + tmp = decodeText(tmp.replace(RE_ENCODED, encodedReplacer), + 'binary', + charset) + } + charset = '' + } else if (tmp.length) { + tmp = decodeText(tmp, 'binary', 'utf8') + } + if (res[p] === undefined) { res[p] = tmp } else { res[p][1] = tmp } + tmp = '' + ++p + continue + } else if (!inquote && (char === ' ' || char === '\t')) { continue } + } + tmp += char + } + if (charset && tmp.length) { + tmp = decodeText(tmp.replace(RE_ENCODED, encodedReplacer), + 'binary', + charset) + } else if (tmp) { + tmp = decodeText(tmp, 'binary', 'utf8') + } + + if (res[p] === undefined) { + if (tmp) { res[p] = tmp } + } else { res[p][1] = tmp } + + return res +} + +module.exports = parseParams + + +/***/ }), + +/***/ 334: +/***/ ((module) => { + +"use strict"; + +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// pkg/dist-src/index.js +var dist_src_exports = {}; +__export(dist_src_exports, { + createTokenAuth: () => createTokenAuth +}); +module.exports = __toCommonJS(dist_src_exports); + +// pkg/dist-src/auth.js +var REGEX_IS_INSTALLATION_LEGACY = /^v1\./; +var REGEX_IS_INSTALLATION = /^ghs_/; +var REGEX_IS_USER_TO_SERVER = /^ghu_/; +async function auth(token) { + const isApp = token.split(/\./).length === 3; + const isInstallation = REGEX_IS_INSTALLATION_LEGACY.test(token) || REGEX_IS_INSTALLATION.test(token); + const isUserToServer = REGEX_IS_USER_TO_SERVER.test(token); + const tokenType = isApp ? "app" : isInstallation ? "installation" : isUserToServer ? "user-to-server" : "oauth"; + return { + type: "token", + token, + tokenType + }; +} + +// pkg/dist-src/with-authorization-prefix.js +function withAuthorizationPrefix(token) { + if (token.split(/\./).length === 3) { + return `bearer ${token}`; + } + return `token ${token}`; +} + +// pkg/dist-src/hook.js +async function hook(token, request, route, parameters) { + const endpoint = request.endpoint.merge( + route, + parameters + ); + endpoint.headers.authorization = withAuthorizationPrefix(token); + return request(endpoint); +} + +// pkg/dist-src/index.js +var createTokenAuth = function createTokenAuth2(token) { + if (!token) { + throw new Error("[@octokit/auth-token] No token passed to createTokenAuth"); + } + if (typeof token !== "string") { + throw new Error( + "[@octokit/auth-token] Token passed to createTokenAuth is not a string" + ); + } + token = token.replace(/^(token|bearer) +/i, ""); + return Object.assign(auth.bind(null, token), { + hook: hook.bind(null, token) + }); +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (0); + + +/***/ }), + +/***/ 6762: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// pkg/dist-src/index.js +var dist_src_exports = {}; +__export(dist_src_exports, { + Octokit: () => Octokit +}); +module.exports = __toCommonJS(dist_src_exports); +var import_universal_user_agent = __nccwpck_require__(5030); +var import_before_after_hook = __nccwpck_require__(3682); +var import_request = __nccwpck_require__(6234); +var import_graphql = __nccwpck_require__(8467); +var import_auth_token = __nccwpck_require__(334); + +// pkg/dist-src/version.js +var VERSION = "5.0.1"; + +// pkg/dist-src/index.js +var Octokit = class { + static { + this.VERSION = VERSION; + } + static defaults(defaults) { + const OctokitWithDefaults = class extends this { + constructor(...args) { + const options = args[0] || {}; + if (typeof defaults === "function") { + super(defaults(options)); + return; + } + super( + Object.assign( + {}, + defaults, + options, + options.userAgent && defaults.userAgent ? { + userAgent: `${options.userAgent} ${defaults.userAgent}` + } : null + ) + ); + } + }; + return OctokitWithDefaults; + } + static { + this.plugins = []; + } + /** + * Attach a plugin (or many) to your Octokit instance. + * + * @example + * const API = Octokit.plugin(plugin1, plugin2, plugin3, ...) + */ + static plugin(...newPlugins) { + const currentPlugins = this.plugins; + const NewOctokit = class extends this { + static { + this.plugins = currentPlugins.concat( + newPlugins.filter((plugin) => !currentPlugins.includes(plugin)) + ); + } + }; + return NewOctokit; + } + constructor(options = {}) { + const hook = new import_before_after_hook.Collection(); + const requestDefaults = { + baseUrl: import_request.request.endpoint.DEFAULTS.baseUrl, + headers: {}, + request: Object.assign({}, options.request, { + // @ts-ignore internal usage only, no need to type + hook: hook.bind(null, "request") + }), + mediaType: { + previews: [], + format: "" + } + }; + requestDefaults.headers["user-agent"] = [ + options.userAgent, + `octokit-core.js/${VERSION} ${(0, import_universal_user_agent.getUserAgent)()}` + ].filter(Boolean).join(" "); + if (options.baseUrl) { + requestDefaults.baseUrl = options.baseUrl; + } + if (options.previews) { + requestDefaults.mediaType.previews = options.previews; + } + if (options.timeZone) { + requestDefaults.headers["time-zone"] = options.timeZone; + } + this.request = import_request.request.defaults(requestDefaults); + this.graphql = (0, import_graphql.withCustomRequest)(this.request).defaults(requestDefaults); + this.log = Object.assign( + { + debug: () => { + }, + info: () => { + }, + warn: console.warn.bind(console), + error: console.error.bind(console) + }, + options.log + ); + this.hook = hook; + if (!options.authStrategy) { + if (!options.auth) { + this.auth = async () => ({ + type: "unauthenticated" + }); + } else { + const auth = (0, import_auth_token.createTokenAuth)(options.auth); + hook.wrap("request", auth.hook); + this.auth = auth; + } + } else { + const { authStrategy, ...otherOptions } = options; + const auth = authStrategy( + Object.assign( + { + request: this.request, + log: this.log, + // we pass the current octokit instance as well as its constructor options + // to allow for authentication strategies that return a new octokit instance + // that shares the same internal state as the current one. The original + // requirement for this was the "event-octokit" authentication strategy + // of https://github.com/probot/octokit-auth-probot. + octokit: this, + octokitOptions: otherOptions + }, + options.auth + ) + ); + hook.wrap("request", auth.hook); + this.auth = auth; + } + const classConstructor = this.constructor; + classConstructor.plugins.forEach((plugin) => { + Object.assign(this, plugin(this, options)); + }); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (0); + + +/***/ }), + +/***/ 9440: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// pkg/dist-src/index.js +var dist_src_exports = {}; +__export(dist_src_exports, { + endpoint: () => endpoint +}); +module.exports = __toCommonJS(dist_src_exports); + +// pkg/dist-src/defaults.js +var import_universal_user_agent = __nccwpck_require__(5030); + +// pkg/dist-src/version.js +var VERSION = "9.0.2"; + +// pkg/dist-src/defaults.js +var userAgent = `octokit-endpoint.js/${VERSION} ${(0, import_universal_user_agent.getUserAgent)()}`; +var DEFAULTS = { + method: "GET", + baseUrl: "https://api.github.com", + headers: { + accept: "application/vnd.github.v3+json", + "user-agent": userAgent + }, + mediaType: { + format: "" + } +}; + +// pkg/dist-src/util/lowercase-keys.js +function lowercaseKeys(object) { + if (!object) { + return {}; + } + return Object.keys(object).reduce((newObj, key) => { + newObj[key.toLowerCase()] = object[key]; + return newObj; + }, {}); +} + +// pkg/dist-src/util/merge-deep.js +var import_is_plain_object = __nccwpck_require__(3287); +function mergeDeep(defaults, options) { + const result = Object.assign({}, defaults); + Object.keys(options).forEach((key) => { + if ((0, import_is_plain_object.isPlainObject)(options[key])) { + if (!(key in defaults)) + Object.assign(result, { [key]: options[key] }); + else + result[key] = mergeDeep(defaults[key], options[key]); + } else { + Object.assign(result, { [key]: options[key] }); + } + }); + return result; +} + +// pkg/dist-src/util/remove-undefined-properties.js +function removeUndefinedProperties(obj) { + for (const key in obj) { + if (obj[key] === void 0) { + delete obj[key]; + } + } + return obj; +} + +// pkg/dist-src/merge.js +function merge(defaults, route, options) { + if (typeof route === "string") { + let [method, url] = route.split(" "); + options = Object.assign(url ? { method, url } : { url: method }, options); + } else { + options = Object.assign({}, route); + } + options.headers = lowercaseKeys(options.headers); + removeUndefinedProperties(options); + removeUndefinedProperties(options.headers); + const mergedOptions = mergeDeep(defaults || {}, options); + if (options.url === "/graphql") { + if (defaults && defaults.mediaType.previews?.length) { + mergedOptions.mediaType.previews = defaults.mediaType.previews.filter( + (preview) => !mergedOptions.mediaType.previews.includes(preview) + ).concat(mergedOptions.mediaType.previews); + } + mergedOptions.mediaType.previews = (mergedOptions.mediaType.previews || []).map((preview) => preview.replace(/-preview/, "")); + } + return mergedOptions; +} + +// pkg/dist-src/util/add-query-parameters.js +function addQueryParameters(url, parameters) { + const separator = /\?/.test(url) ? "&" : "?"; + const names = Object.keys(parameters); + if (names.length === 0) { + return url; + } + return url + separator + names.map((name) => { + if (name === "q") { + return "q=" + parameters.q.split("+").map(encodeURIComponent).join("+"); + } + return `${name}=${encodeURIComponent(parameters[name])}`; + }).join("&"); +} + +// pkg/dist-src/util/extract-url-variable-names.js +var urlVariableRegex = /\{[^}]+\}/g; +function removeNonChars(variableName) { + return variableName.replace(/^\W+|\W+$/g, "").split(/,/); +} +function extractUrlVariableNames(url) { + const matches = url.match(urlVariableRegex); + if (!matches) { + return []; + } + return matches.map(removeNonChars).reduce((a, b) => a.concat(b), []); +} + +// pkg/dist-src/util/omit.js +function omit(object, keysToOmit) { + return Object.keys(object).filter((option) => !keysToOmit.includes(option)).reduce((obj, key) => { + obj[key] = object[key]; + return obj; + }, {}); +} + +// pkg/dist-src/util/url-template.js +function encodeReserved(str) { + return str.split(/(%[0-9A-Fa-f]{2})/g).map(function(part) { + if (!/%[0-9A-Fa-f]/.test(part)) { + part = encodeURI(part).replace(/%5B/g, "[").replace(/%5D/g, "]"); + } + return part; + }).join(""); +} +function encodeUnreserved(str) { + return encodeURIComponent(str).replace(/[!'()*]/g, function(c) { + return "%" + c.charCodeAt(0).toString(16).toUpperCase(); + }); +} +function encodeValue(operator, value, key) { + value = operator === "+" || operator === "#" ? encodeReserved(value) : encodeUnreserved(value); + if (key) { + return encodeUnreserved(key) + "=" + value; + } else { + return value; + } +} +function isDefined(value) { + return value !== void 0 && value !== null; +} +function isKeyOperator(operator) { + return operator === ";" || operator === "&" || operator === "?"; +} +function getValues(context, operator, key, modifier) { + var value = context[key], result = []; + if (isDefined(value) && value !== "") { + if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") { + value = value.toString(); + if (modifier && modifier !== "*") { + value = value.substring(0, parseInt(modifier, 10)); + } + result.push( + encodeValue(operator, value, isKeyOperator(operator) ? key : "") + ); + } else { + if (modifier === "*") { + if (Array.isArray(value)) { + value.filter(isDefined).forEach(function(value2) { + result.push( + encodeValue(operator, value2, isKeyOperator(operator) ? key : "") + ); + }); + } else { + Object.keys(value).forEach(function(k) { + if (isDefined(value[k])) { + result.push(encodeValue(operator, value[k], k)); + } + }); + } + } else { + const tmp = []; + if (Array.isArray(value)) { + value.filter(isDefined).forEach(function(value2) { + tmp.push(encodeValue(operator, value2)); + }); + } else { + Object.keys(value).forEach(function(k) { + if (isDefined(value[k])) { + tmp.push(encodeUnreserved(k)); + tmp.push(encodeValue(operator, value[k].toString())); + } + }); + } + if (isKeyOperator(operator)) { + result.push(encodeUnreserved(key) + "=" + tmp.join(",")); + } else if (tmp.length !== 0) { + result.push(tmp.join(",")); + } + } + } + } else { + if (operator === ";") { + if (isDefined(value)) { + result.push(encodeUnreserved(key)); + } + } else if (value === "" && (operator === "&" || operator === "?")) { + result.push(encodeUnreserved(key) + "="); + } else if (value === "") { + result.push(""); + } + } + return result; +} +function parseUrl(template) { + return { + expand: expand.bind(null, template) + }; +} +function expand(template, context) { + var operators = ["+", "#", ".", "/", ";", "?", "&"]; + template = template.replace( + /\{([^\{\}]+)\}|([^\{\}]+)/g, + function(_, expression, literal) { + if (expression) { + let operator = ""; + const values = []; + if (operators.indexOf(expression.charAt(0)) !== -1) { + operator = expression.charAt(0); + expression = expression.substr(1); + } + expression.split(/,/g).forEach(function(variable) { + var tmp = /([^:\*]*)(?::(\d+)|(\*))?/.exec(variable); + values.push(getValues(context, operator, tmp[1], tmp[2] || tmp[3])); + }); + if (operator && operator !== "+") { + var separator = ","; + if (operator === "?") { + separator = "&"; + } else if (operator !== "#") { + separator = operator; + } + return (values.length !== 0 ? operator : "") + values.join(separator); + } else { + return values.join(","); + } + } else { + return encodeReserved(literal); + } + } + ); + if (template === "/") { + return template; + } else { + return template.replace(/\/$/, ""); + } +} + +// pkg/dist-src/parse.js +function parse(options) { + let method = options.method.toUpperCase(); + let url = (options.url || "/").replace(/:([a-z]\w+)/g, "{$1}"); + let headers = Object.assign({}, options.headers); + let body; + let parameters = omit(options, [ + "method", + "baseUrl", + "url", + "headers", + "request", + "mediaType" + ]); + const urlVariableNames = extractUrlVariableNames(url); + url = parseUrl(url).expand(parameters); + if (!/^http/.test(url)) { + url = options.baseUrl + url; + } + const omittedParameters = Object.keys(options).filter((option) => urlVariableNames.includes(option)).concat("baseUrl"); + const remainingParameters = omit(parameters, omittedParameters); + const isBinaryRequest = /application\/octet-stream/i.test(headers.accept); + if (!isBinaryRequest) { + if (options.mediaType.format) { + headers.accept = headers.accept.split(/,/).map( + (format) => format.replace( + /application\/vnd(\.\w+)(\.v3)?(\.\w+)?(\+json)?$/, + `application/vnd$1$2.${options.mediaType.format}` + ) + ).join(","); + } + if (url.endsWith("/graphql")) { + if (options.mediaType.previews?.length) { + const previewsFromAcceptHeader = headers.accept.match(/[\w-]+(?=-preview)/g) || []; + headers.accept = previewsFromAcceptHeader.concat(options.mediaType.previews).map((preview) => { + const format = options.mediaType.format ? `.${options.mediaType.format}` : "+json"; + return `application/vnd.github.${preview}-preview${format}`; + }).join(","); + } + } + } + if (["GET", "HEAD"].includes(method)) { + url = addQueryParameters(url, remainingParameters); + } else { + if ("data" in remainingParameters) { + body = remainingParameters.data; + } else { + if (Object.keys(remainingParameters).length) { + body = remainingParameters; + } + } + } + if (!headers["content-type"] && typeof body !== "undefined") { + headers["content-type"] = "application/json; charset=utf-8"; + } + if (["PATCH", "PUT"].includes(method) && typeof body === "undefined") { + body = ""; + } + return Object.assign( + { method, url, headers }, + typeof body !== "undefined" ? { body } : null, + options.request ? { request: options.request } : null + ); +} + +// pkg/dist-src/endpoint-with-defaults.js +function endpointWithDefaults(defaults, route, options) { + return parse(merge(defaults, route, options)); +} + +// pkg/dist-src/with-defaults.js +function withDefaults(oldDefaults, newDefaults) { + const DEFAULTS2 = merge(oldDefaults, newDefaults); + const endpoint2 = endpointWithDefaults.bind(null, DEFAULTS2); + return Object.assign(endpoint2, { + DEFAULTS: DEFAULTS2, + defaults: withDefaults.bind(null, DEFAULTS2), + merge: merge.bind(null, DEFAULTS2), + parse + }); +} + +// pkg/dist-src/index.js +var endpoint = withDefaults(null, DEFAULTS); +// Annotate the CommonJS export names for ESM import in node: +0 && (0); + + +/***/ }), + +/***/ 8467: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// pkg/dist-src/index.js +var dist_src_exports = {}; +__export(dist_src_exports, { + GraphqlResponseError: () => GraphqlResponseError, + graphql: () => graphql2, + withCustomRequest: () => withCustomRequest +}); +module.exports = __toCommonJS(dist_src_exports); +var import_request3 = __nccwpck_require__(6234); +var import_universal_user_agent = __nccwpck_require__(5030); + +// pkg/dist-src/version.js +var VERSION = "7.0.2"; + +// pkg/dist-src/with-defaults.js +var import_request2 = __nccwpck_require__(6234); + +// pkg/dist-src/graphql.js +var import_request = __nccwpck_require__(6234); + +// pkg/dist-src/error.js +function _buildMessageForResponseErrors(data) { + return `Request failed due to following response errors: +` + data.errors.map((e) => ` - ${e.message}`).join("\n"); +} +var GraphqlResponseError = class extends Error { + constructor(request2, headers, response) { + super(_buildMessageForResponseErrors(response)); + this.request = request2; + this.headers = headers; + this.response = response; + this.name = "GraphqlResponseError"; + this.errors = response.errors; + this.data = response.data; + if (Error.captureStackTrace) { + Error.captureStackTrace(this, this.constructor); + } + } +}; + +// pkg/dist-src/graphql.js +var NON_VARIABLE_OPTIONS = [ + "method", + "baseUrl", + "url", + "headers", + "request", + "query", + "mediaType" +]; +var FORBIDDEN_VARIABLE_OPTIONS = ["query", "method", "url"]; +var GHES_V3_SUFFIX_REGEX = /\/api\/v3\/?$/; +function graphql(request2, query, options) { + if (options) { + if (typeof query === "string" && "query" in options) { + return Promise.reject( + new Error(`[@octokit/graphql] "query" cannot be used as variable name`) + ); + } + for (const key in options) { + if (!FORBIDDEN_VARIABLE_OPTIONS.includes(key)) + continue; + return Promise.reject( + new Error( + `[@octokit/graphql] "${key}" cannot be used as variable name` + ) + ); + } + } + const parsedOptions = typeof query === "string" ? Object.assign({ query }, options) : query; + const requestOptions = Object.keys( + parsedOptions + ).reduce((result, key) => { + if (NON_VARIABLE_OPTIONS.includes(key)) { + result[key] = parsedOptions[key]; + return result; + } + if (!result.variables) { + result.variables = {}; + } + result.variables[key] = parsedOptions[key]; + return result; + }, {}); + const baseUrl = parsedOptions.baseUrl || request2.endpoint.DEFAULTS.baseUrl; + if (GHES_V3_SUFFIX_REGEX.test(baseUrl)) { + requestOptions.url = baseUrl.replace(GHES_V3_SUFFIX_REGEX, "/api/graphql"); + } + return request2(requestOptions).then((response) => { + if (response.data.errors) { + const headers = {}; + for (const key of Object.keys(response.headers)) { + headers[key] = response.headers[key]; + } + throw new GraphqlResponseError( + requestOptions, + headers, + response.data + ); + } + return response.data.data; + }); +} + +// pkg/dist-src/with-defaults.js +function withDefaults(request2, newDefaults) { + const newRequest = request2.defaults(newDefaults); + const newApi = (query, options) => { + return graphql(newRequest, query, options); + }; + return Object.assign(newApi, { + defaults: withDefaults.bind(null, newRequest), + endpoint: newRequest.endpoint + }); +} + +// pkg/dist-src/index.js +var graphql2 = withDefaults(import_request3.request, { + headers: { + "user-agent": `octokit-graphql.js/${VERSION} ${(0, import_universal_user_agent.getUserAgent)()}` + }, + method: "POST", + url: "/graphql" +}); +function withCustomRequest(customRequest) { + return withDefaults(customRequest, { + method: "POST", + url: "/graphql" + }); +} +// Annotate the CommonJS export names for ESM import in node: +0 && (0); + + +/***/ }), + +/***/ 4193: +/***/ ((module) => { + +"use strict"; + +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// pkg/dist-src/index.js +var dist_src_exports = {}; +__export(dist_src_exports, { + composePaginateRest: () => composePaginateRest, + isPaginatingEndpoint: () => isPaginatingEndpoint, + paginateRest: () => paginateRest, + paginatingEndpoints: () => paginatingEndpoints +}); +module.exports = __toCommonJS(dist_src_exports); + +// pkg/dist-src/version.js +var VERSION = "9.1.2"; + +// pkg/dist-src/normalize-paginated-list-response.js +function normalizePaginatedListResponse(response) { + if (!response.data) { + return { + ...response, + data: [] + }; + } + const responseNeedsNormalization = "total_count" in response.data && !("url" in response.data); + if (!responseNeedsNormalization) + return response; + const incompleteResults = response.data.incomplete_results; + const repositorySelection = response.data.repository_selection; + const totalCount = response.data.total_count; + delete response.data.incomplete_results; + delete response.data.repository_selection; + delete response.data.total_count; + const namespaceKey = Object.keys(response.data)[0]; + const data = response.data[namespaceKey]; + response.data = data; + if (typeof incompleteResults !== "undefined") { + response.data.incomplete_results = incompleteResults; + } + if (typeof repositorySelection !== "undefined") { + response.data.repository_selection = repositorySelection; + } + response.data.total_count = totalCount; + return response; +} + +// pkg/dist-src/iterator.js +function iterator(octokit, route, parameters) { + const options = typeof route === "function" ? route.endpoint(parameters) : octokit.request.endpoint(route, parameters); + const requestMethod = typeof route === "function" ? route : octokit.request; + const method = options.method; + const headers = options.headers; + let url = options.url; + return { + [Symbol.asyncIterator]: () => ({ + async next() { + if (!url) + return { done: true }; + try { + const response = await requestMethod({ method, url, headers }); + const normalizedResponse = normalizePaginatedListResponse(response); + url = ((normalizedResponse.headers.link || "").match( + /<([^>]+)>;\s*rel="next"/ + ) || [])[1]; + return { value: normalizedResponse }; + } catch (error) { + if (error.status !== 409) + throw error; + url = ""; + return { + value: { + status: 200, + headers: {}, + data: [] + } + }; + } + } + }) + }; +} + +// pkg/dist-src/paginate.js +function paginate(octokit, route, parameters, mapFn) { + if (typeof parameters === "function") { + mapFn = parameters; + parameters = void 0; + } + return gather( + octokit, + [], + iterator(octokit, route, parameters)[Symbol.asyncIterator](), + mapFn + ); +} +function gather(octokit, results, iterator2, mapFn) { + return iterator2.next().then((result) => { + if (result.done) { + return results; + } + let earlyExit = false; + function done() { + earlyExit = true; + } + results = results.concat( + mapFn ? mapFn(result.value, done) : result.value.data + ); + if (earlyExit) { + return results; + } + return gather(octokit, results, iterator2, mapFn); + }); +} + +// pkg/dist-src/compose-paginate.js +var composePaginateRest = Object.assign(paginate, { + iterator +}); + +// pkg/dist-src/generated/paginating-endpoints.js +var paginatingEndpoints = [ + "GET /advisories", + "GET /app/hook/deliveries", + "GET /app/installation-requests", + "GET /app/installations", + "GET /assignments/{assignment_id}/accepted_assignments", + "GET /classrooms", + "GET /classrooms/{classroom_id}/assignments", + "GET /enterprises/{enterprise}/dependabot/alerts", + "GET /enterprises/{enterprise}/secret-scanning/alerts", + "GET /events", + "GET /gists", + "GET /gists/public", + "GET /gists/starred", + "GET /gists/{gist_id}/comments", + "GET /gists/{gist_id}/commits", + "GET /gists/{gist_id}/forks", + "GET /installation/repositories", + "GET /issues", + "GET /licenses", + "GET /marketplace_listing/plans", + "GET /marketplace_listing/plans/{plan_id}/accounts", + "GET /marketplace_listing/stubbed/plans", + "GET /marketplace_listing/stubbed/plans/{plan_id}/accounts", + "GET /networks/{owner}/{repo}/events", + "GET /notifications", + "GET /organizations", + "GET /orgs/{org}/actions/cache/usage-by-repository", + "GET /orgs/{org}/actions/permissions/repositories", + "GET /orgs/{org}/actions/runners", + "GET /orgs/{org}/actions/secrets", + "GET /orgs/{org}/actions/secrets/{secret_name}/repositories", + "GET /orgs/{org}/actions/variables", + "GET /orgs/{org}/actions/variables/{name}/repositories", + "GET /orgs/{org}/blocks", + "GET /orgs/{org}/code-scanning/alerts", + "GET /orgs/{org}/codespaces", + "GET /orgs/{org}/codespaces/secrets", + "GET /orgs/{org}/codespaces/secrets/{secret_name}/repositories", + "GET /orgs/{org}/copilot/billing/seats", + "GET /orgs/{org}/dependabot/alerts", + "GET /orgs/{org}/dependabot/secrets", + "GET /orgs/{org}/dependabot/secrets/{secret_name}/repositories", + "GET /orgs/{org}/events", + "GET /orgs/{org}/failed_invitations", + "GET /orgs/{org}/hooks", + "GET /orgs/{org}/hooks/{hook_id}/deliveries", + "GET /orgs/{org}/installations", + "GET /orgs/{org}/invitations", + "GET /orgs/{org}/invitations/{invitation_id}/teams", + "GET /orgs/{org}/issues", + "GET /orgs/{org}/members", + "GET /orgs/{org}/members/{username}/codespaces", + "GET /orgs/{org}/migrations", + "GET /orgs/{org}/migrations/{migration_id}/repositories", + "GET /orgs/{org}/outside_collaborators", + "GET /orgs/{org}/packages", + "GET /orgs/{org}/packages/{package_type}/{package_name}/versions", + "GET /orgs/{org}/personal-access-token-requests", + "GET /orgs/{org}/personal-access-token-requests/{pat_request_id}/repositories", + "GET /orgs/{org}/personal-access-tokens", + "GET /orgs/{org}/personal-access-tokens/{pat_id}/repositories", + "GET /orgs/{org}/projects", + "GET /orgs/{org}/properties/values", + "GET /orgs/{org}/public_members", + "GET /orgs/{org}/repos", + "GET /orgs/{org}/rulesets", + "GET /orgs/{org}/rulesets/rule-suites", + "GET /orgs/{org}/secret-scanning/alerts", + "GET /orgs/{org}/security-advisories", + "GET /orgs/{org}/teams", + "GET /orgs/{org}/teams/{team_slug}/discussions", + "GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments", + "GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}/reactions", + "GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/reactions", + "GET /orgs/{org}/teams/{team_slug}/invitations", + "GET /orgs/{org}/teams/{team_slug}/members", + "GET /orgs/{org}/teams/{team_slug}/projects", + "GET /orgs/{org}/teams/{team_slug}/repos", + "GET /orgs/{org}/teams/{team_slug}/teams", + "GET /projects/columns/{column_id}/cards", + "GET /projects/{project_id}/collaborators", + "GET /projects/{project_id}/columns", + "GET /repos/{owner}/{repo}/actions/artifacts", + "GET /repos/{owner}/{repo}/actions/caches", + "GET /repos/{owner}/{repo}/actions/organization-secrets", + "GET /repos/{owner}/{repo}/actions/organization-variables", + "GET /repos/{owner}/{repo}/actions/runners", + "GET /repos/{owner}/{repo}/actions/runs", + "GET /repos/{owner}/{repo}/actions/runs/{run_id}/artifacts", + "GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number}/jobs", + "GET /repos/{owner}/{repo}/actions/runs/{run_id}/jobs", + "GET /repos/{owner}/{repo}/actions/secrets", + "GET /repos/{owner}/{repo}/actions/variables", + "GET /repos/{owner}/{repo}/actions/workflows", + "GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs", + "GET /repos/{owner}/{repo}/activity", + "GET /repos/{owner}/{repo}/assignees", + "GET /repos/{owner}/{repo}/branches", + "GET /repos/{owner}/{repo}/check-runs/{check_run_id}/annotations", + "GET /repos/{owner}/{repo}/check-suites/{check_suite_id}/check-runs", + "GET /repos/{owner}/{repo}/code-scanning/alerts", + "GET /repos/{owner}/{repo}/code-scanning/alerts/{alert_number}/instances", + "GET /repos/{owner}/{repo}/code-scanning/analyses", + "GET /repos/{owner}/{repo}/codespaces", + "GET /repos/{owner}/{repo}/codespaces/devcontainers", + "GET /repos/{owner}/{repo}/codespaces/secrets", + "GET /repos/{owner}/{repo}/collaborators", + "GET /repos/{owner}/{repo}/comments", + "GET /repos/{owner}/{repo}/comments/{comment_id}/reactions", + "GET /repos/{owner}/{repo}/commits", + "GET /repos/{owner}/{repo}/commits/{commit_sha}/comments", + "GET /repos/{owner}/{repo}/commits/{commit_sha}/pulls", + "GET /repos/{owner}/{repo}/commits/{ref}/check-runs", + "GET /repos/{owner}/{repo}/commits/{ref}/check-suites", + "GET /repos/{owner}/{repo}/commits/{ref}/status", + "GET /repos/{owner}/{repo}/commits/{ref}/statuses", + "GET /repos/{owner}/{repo}/contributors", + "GET /repos/{owner}/{repo}/dependabot/alerts", + "GET /repos/{owner}/{repo}/dependabot/secrets", + "GET /repos/{owner}/{repo}/deployments", + "GET /repos/{owner}/{repo}/deployments/{deployment_id}/statuses", + "GET /repos/{owner}/{repo}/environments", + "GET /repos/{owner}/{repo}/environments/{environment_name}/deployment-branch-policies", + "GET /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules/apps", + "GET /repos/{owner}/{repo}/events", + "GET /repos/{owner}/{repo}/forks", + "GET /repos/{owner}/{repo}/hooks", + "GET /repos/{owner}/{repo}/hooks/{hook_id}/deliveries", + "GET /repos/{owner}/{repo}/invitations", + "GET /repos/{owner}/{repo}/issues", + "GET /repos/{owner}/{repo}/issues/comments", + "GET /repos/{owner}/{repo}/issues/comments/{comment_id}/reactions", + "GET /repos/{owner}/{repo}/issues/events", + "GET /repos/{owner}/{repo}/issues/{issue_number}/comments", + "GET /repos/{owner}/{repo}/issues/{issue_number}/events", + "GET /repos/{owner}/{repo}/issues/{issue_number}/labels", + "GET /repos/{owner}/{repo}/issues/{issue_number}/reactions", + "GET /repos/{owner}/{repo}/issues/{issue_number}/timeline", + "GET /repos/{owner}/{repo}/keys", + "GET /repos/{owner}/{repo}/labels", + "GET /repos/{owner}/{repo}/milestones", + "GET /repos/{owner}/{repo}/milestones/{milestone_number}/labels", + "GET /repos/{owner}/{repo}/notifications", + "GET /repos/{owner}/{repo}/pages/builds", + "GET /repos/{owner}/{repo}/projects", + "GET /repos/{owner}/{repo}/pulls", + "GET /repos/{owner}/{repo}/pulls/comments", + "GET /repos/{owner}/{repo}/pulls/comments/{comment_id}/reactions", + "GET /repos/{owner}/{repo}/pulls/{pull_number}/comments", + "GET /repos/{owner}/{repo}/pulls/{pull_number}/commits", + "GET /repos/{owner}/{repo}/pulls/{pull_number}/files", + "GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews", + "GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/comments", + "GET /repos/{owner}/{repo}/releases", + "GET /repos/{owner}/{repo}/releases/{release_id}/assets", + "GET /repos/{owner}/{repo}/releases/{release_id}/reactions", + "GET /repos/{owner}/{repo}/rules/branches/{branch}", + "GET /repos/{owner}/{repo}/rulesets", + "GET /repos/{owner}/{repo}/rulesets/rule-suites", + "GET /repos/{owner}/{repo}/secret-scanning/alerts", + "GET /repos/{owner}/{repo}/secret-scanning/alerts/{alert_number}/locations", + "GET /repos/{owner}/{repo}/security-advisories", + "GET /repos/{owner}/{repo}/stargazers", + "GET /repos/{owner}/{repo}/subscribers", + "GET /repos/{owner}/{repo}/tags", + "GET /repos/{owner}/{repo}/teams", + "GET /repos/{owner}/{repo}/topics", + "GET /repositories", + "GET /repositories/{repository_id}/environments/{environment_name}/secrets", + "GET /repositories/{repository_id}/environments/{environment_name}/variables", + "GET /search/code", + "GET /search/commits", + "GET /search/issues", + "GET /search/labels", + "GET /search/repositories", + "GET /search/topics", + "GET /search/users", + "GET /teams/{team_id}/discussions", + "GET /teams/{team_id}/discussions/{discussion_number}/comments", + "GET /teams/{team_id}/discussions/{discussion_number}/comments/{comment_number}/reactions", + "GET /teams/{team_id}/discussions/{discussion_number}/reactions", + "GET /teams/{team_id}/invitations", + "GET /teams/{team_id}/members", + "GET /teams/{team_id}/projects", + "GET /teams/{team_id}/repos", + "GET /teams/{team_id}/teams", + "GET /user/blocks", + "GET /user/codespaces", + "GET /user/codespaces/secrets", + "GET /user/emails", + "GET /user/followers", + "GET /user/following", + "GET /user/gpg_keys", + "GET /user/installations", + "GET /user/installations/{installation_id}/repositories", + "GET /user/issues", + "GET /user/keys", + "GET /user/marketplace_purchases", + "GET /user/marketplace_purchases/stubbed", + "GET /user/memberships/orgs", + "GET /user/migrations", + "GET /user/migrations/{migration_id}/repositories", + "GET /user/orgs", + "GET /user/packages", + "GET /user/packages/{package_type}/{package_name}/versions", + "GET /user/public_emails", + "GET /user/repos", + "GET /user/repository_invitations", + "GET /user/social_accounts", + "GET /user/ssh_signing_keys", + "GET /user/starred", + "GET /user/subscriptions", + "GET /user/teams", + "GET /users", + "GET /users/{username}/events", + "GET /users/{username}/events/orgs/{org}", + "GET /users/{username}/events/public", + "GET /users/{username}/followers", + "GET /users/{username}/following", + "GET /users/{username}/gists", + "GET /users/{username}/gpg_keys", + "GET /users/{username}/keys", + "GET /users/{username}/orgs", + "GET /users/{username}/packages", + "GET /users/{username}/projects", + "GET /users/{username}/received_events", + "GET /users/{username}/received_events/public", + "GET /users/{username}/repos", + "GET /users/{username}/social_accounts", + "GET /users/{username}/ssh_signing_keys", + "GET /users/{username}/starred", + "GET /users/{username}/subscriptions" +]; + +// pkg/dist-src/paginating-endpoints.js +function isPaginatingEndpoint(arg) { + if (typeof arg === "string") { + return paginatingEndpoints.includes(arg); + } else { + return false; + } +} + +// pkg/dist-src/index.js +function paginateRest(octokit) { + return { + paginate: Object.assign(paginate.bind(null, octokit), { + iterator: iterator.bind(null, octokit) + }) + }; +} +paginateRest.VERSION = VERSION; +// Annotate the CommonJS export names for ESM import in node: +0 && (0); + + +/***/ }), + +/***/ 3044: +/***/ ((module) => { + +"use strict"; + +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// pkg/dist-src/index.js +var dist_src_exports = {}; +__export(dist_src_exports, { + legacyRestEndpointMethods: () => legacyRestEndpointMethods, + restEndpointMethods: () => restEndpointMethods +}); +module.exports = __toCommonJS(dist_src_exports); + +// pkg/dist-src/version.js +var VERSION = "10.1.2"; + +// pkg/dist-src/generated/endpoints.js +var Endpoints = { + actions: { + addCustomLabelsToSelfHostedRunnerForOrg: [ + "POST /orgs/{org}/actions/runners/{runner_id}/labels" + ], + addCustomLabelsToSelfHostedRunnerForRepo: [ + "POST /repos/{owner}/{repo}/actions/runners/{runner_id}/labels" + ], + addSelectedRepoToOrgSecret: [ + "PUT /orgs/{org}/actions/secrets/{secret_name}/repositories/{repository_id}" + ], + addSelectedRepoToOrgVariable: [ + "PUT /orgs/{org}/actions/variables/{name}/repositories/{repository_id}" + ], + approveWorkflowRun: [ + "POST /repos/{owner}/{repo}/actions/runs/{run_id}/approve" + ], + cancelWorkflowRun: [ + "POST /repos/{owner}/{repo}/actions/runs/{run_id}/cancel" + ], + createEnvironmentVariable: [ + "POST /repositories/{repository_id}/environments/{environment_name}/variables" + ], + createOrUpdateEnvironmentSecret: [ + "PUT /repositories/{repository_id}/environments/{environment_name}/secrets/{secret_name}" + ], + createOrUpdateOrgSecret: ["PUT /orgs/{org}/actions/secrets/{secret_name}"], + createOrUpdateRepoSecret: [ + "PUT /repos/{owner}/{repo}/actions/secrets/{secret_name}" + ], + createOrgVariable: ["POST /orgs/{org}/actions/variables"], + createRegistrationTokenForOrg: [ + "POST /orgs/{org}/actions/runners/registration-token" + ], + createRegistrationTokenForRepo: [ + "POST /repos/{owner}/{repo}/actions/runners/registration-token" + ], + createRemoveTokenForOrg: ["POST /orgs/{org}/actions/runners/remove-token"], + createRemoveTokenForRepo: [ + "POST /repos/{owner}/{repo}/actions/runners/remove-token" + ], + createRepoVariable: ["POST /repos/{owner}/{repo}/actions/variables"], + createWorkflowDispatch: [ + "POST /repos/{owner}/{repo}/actions/workflows/{workflow_id}/dispatches" + ], + deleteActionsCacheById: [ + "DELETE /repos/{owner}/{repo}/actions/caches/{cache_id}" + ], + deleteActionsCacheByKey: [ + "DELETE /repos/{owner}/{repo}/actions/caches{?key,ref}" + ], + deleteArtifact: [ + "DELETE /repos/{owner}/{repo}/actions/artifacts/{artifact_id}" + ], + deleteEnvironmentSecret: [ + "DELETE /repositories/{repository_id}/environments/{environment_name}/secrets/{secret_name}" + ], + deleteEnvironmentVariable: [ + "DELETE /repositories/{repository_id}/environments/{environment_name}/variables/{name}" + ], + deleteOrgSecret: ["DELETE /orgs/{org}/actions/secrets/{secret_name}"], + deleteOrgVariable: ["DELETE /orgs/{org}/actions/variables/{name}"], + deleteRepoSecret: [ + "DELETE /repos/{owner}/{repo}/actions/secrets/{secret_name}" + ], + deleteRepoVariable: [ + "DELETE /repos/{owner}/{repo}/actions/variables/{name}" + ], + deleteSelfHostedRunnerFromOrg: [ + "DELETE /orgs/{org}/actions/runners/{runner_id}" + ], + deleteSelfHostedRunnerFromRepo: [ + "DELETE /repos/{owner}/{repo}/actions/runners/{runner_id}" + ], + deleteWorkflowRun: ["DELETE /repos/{owner}/{repo}/actions/runs/{run_id}"], + deleteWorkflowRunLogs: [ + "DELETE /repos/{owner}/{repo}/actions/runs/{run_id}/logs" + ], + disableSelectedRepositoryGithubActionsOrganization: [ + "DELETE /orgs/{org}/actions/permissions/repositories/{repository_id}" + ], + disableWorkflow: [ + "PUT /repos/{owner}/{repo}/actions/workflows/{workflow_id}/disable" + ], + downloadArtifact: [ + "GET /repos/{owner}/{repo}/actions/artifacts/{artifact_id}/{archive_format}" + ], + downloadJobLogsForWorkflowRun: [ + "GET /repos/{owner}/{repo}/actions/jobs/{job_id}/logs" + ], + downloadWorkflowRunAttemptLogs: [ + "GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number}/logs" + ], + downloadWorkflowRunLogs: [ + "GET /repos/{owner}/{repo}/actions/runs/{run_id}/logs" + ], + enableSelectedRepositoryGithubActionsOrganization: [ + "PUT /orgs/{org}/actions/permissions/repositories/{repository_id}" + ], + enableWorkflow: [ + "PUT /repos/{owner}/{repo}/actions/workflows/{workflow_id}/enable" + ], + forceCancelWorkflowRun: [ + "POST /repos/{owner}/{repo}/actions/runs/{run_id}/force-cancel" + ], + generateRunnerJitconfigForOrg: [ + "POST /orgs/{org}/actions/runners/generate-jitconfig" + ], + generateRunnerJitconfigForRepo: [ + "POST /repos/{owner}/{repo}/actions/runners/generate-jitconfig" + ], + getActionsCacheList: ["GET /repos/{owner}/{repo}/actions/caches"], + getActionsCacheUsage: ["GET /repos/{owner}/{repo}/actions/cache/usage"], + getActionsCacheUsageByRepoForOrg: [ + "GET /orgs/{org}/actions/cache/usage-by-repository" + ], + getActionsCacheUsageForOrg: ["GET /orgs/{org}/actions/cache/usage"], + getAllowedActionsOrganization: [ + "GET /orgs/{org}/actions/permissions/selected-actions" + ], + getAllowedActionsRepository: [ + "GET /repos/{owner}/{repo}/actions/permissions/selected-actions" + ], + getArtifact: ["GET /repos/{owner}/{repo}/actions/artifacts/{artifact_id}"], + getEnvironmentPublicKey: [ + "GET /repositories/{repository_id}/environments/{environment_name}/secrets/public-key" + ], + getEnvironmentSecret: [ + "GET /repositories/{repository_id}/environments/{environment_name}/secrets/{secret_name}" + ], + getEnvironmentVariable: [ + "GET /repositories/{repository_id}/environments/{environment_name}/variables/{name}" + ], + getGithubActionsDefaultWorkflowPermissionsOrganization: [ + "GET /orgs/{org}/actions/permissions/workflow" + ], + getGithubActionsDefaultWorkflowPermissionsRepository: [ + "GET /repos/{owner}/{repo}/actions/permissions/workflow" + ], + getGithubActionsPermissionsOrganization: [ + "GET /orgs/{org}/actions/permissions" + ], + getGithubActionsPermissionsRepository: [ + "GET /repos/{owner}/{repo}/actions/permissions" + ], + getJobForWorkflowRun: ["GET /repos/{owner}/{repo}/actions/jobs/{job_id}"], + getOrgPublicKey: ["GET /orgs/{org}/actions/secrets/public-key"], + getOrgSecret: ["GET /orgs/{org}/actions/secrets/{secret_name}"], + getOrgVariable: ["GET /orgs/{org}/actions/variables/{name}"], + getPendingDeploymentsForRun: [ + "GET /repos/{owner}/{repo}/actions/runs/{run_id}/pending_deployments" + ], + getRepoPermissions: [ + "GET /repos/{owner}/{repo}/actions/permissions", + {}, + { renamed: ["actions", "getGithubActionsPermissionsRepository"] } + ], + getRepoPublicKey: ["GET /repos/{owner}/{repo}/actions/secrets/public-key"], + getRepoSecret: ["GET /repos/{owner}/{repo}/actions/secrets/{secret_name}"], + getRepoVariable: ["GET /repos/{owner}/{repo}/actions/variables/{name}"], + getReviewsForRun: [ + "GET /repos/{owner}/{repo}/actions/runs/{run_id}/approvals" + ], + getSelfHostedRunnerForOrg: ["GET /orgs/{org}/actions/runners/{runner_id}"], + getSelfHostedRunnerForRepo: [ + "GET /repos/{owner}/{repo}/actions/runners/{runner_id}" + ], + getWorkflow: ["GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}"], + getWorkflowAccessToRepository: [ + "GET /repos/{owner}/{repo}/actions/permissions/access" + ], + getWorkflowRun: ["GET /repos/{owner}/{repo}/actions/runs/{run_id}"], + getWorkflowRunAttempt: [ + "GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number}" + ], + getWorkflowRunUsage: [ + "GET /repos/{owner}/{repo}/actions/runs/{run_id}/timing" + ], + getWorkflowUsage: [ + "GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/timing" + ], + listArtifactsForRepo: ["GET /repos/{owner}/{repo}/actions/artifacts"], + listEnvironmentSecrets: [ + "GET /repositories/{repository_id}/environments/{environment_name}/secrets" + ], + listEnvironmentVariables: [ + "GET /repositories/{repository_id}/environments/{environment_name}/variables" + ], + listJobsForWorkflowRun: [ + "GET /repos/{owner}/{repo}/actions/runs/{run_id}/jobs" + ], + listJobsForWorkflowRunAttempt: [ + "GET /repos/{owner}/{repo}/actions/runs/{run_id}/attempts/{attempt_number}/jobs" + ], + listLabelsForSelfHostedRunnerForOrg: [ + "GET /orgs/{org}/actions/runners/{runner_id}/labels" + ], + listLabelsForSelfHostedRunnerForRepo: [ + "GET /repos/{owner}/{repo}/actions/runners/{runner_id}/labels" + ], + listOrgSecrets: ["GET /orgs/{org}/actions/secrets"], + listOrgVariables: ["GET /orgs/{org}/actions/variables"], + listRepoOrganizationSecrets: [ + "GET /repos/{owner}/{repo}/actions/organization-secrets" + ], + listRepoOrganizationVariables: [ + "GET /repos/{owner}/{repo}/actions/organization-variables" + ], + listRepoSecrets: ["GET /repos/{owner}/{repo}/actions/secrets"], + listRepoVariables: ["GET /repos/{owner}/{repo}/actions/variables"], + listRepoWorkflows: ["GET /repos/{owner}/{repo}/actions/workflows"], + listRunnerApplicationsForOrg: ["GET /orgs/{org}/actions/runners/downloads"], + listRunnerApplicationsForRepo: [ + "GET /repos/{owner}/{repo}/actions/runners/downloads" + ], + listSelectedReposForOrgSecret: [ + "GET /orgs/{org}/actions/secrets/{secret_name}/repositories" + ], + listSelectedReposForOrgVariable: [ + "GET /orgs/{org}/actions/variables/{name}/repositories" + ], + listSelectedRepositoriesEnabledGithubActionsOrganization: [ + "GET /orgs/{org}/actions/permissions/repositories" + ], + listSelfHostedRunnersForOrg: ["GET /orgs/{org}/actions/runners"], + listSelfHostedRunnersForRepo: ["GET /repos/{owner}/{repo}/actions/runners"], + listWorkflowRunArtifacts: [ + "GET /repos/{owner}/{repo}/actions/runs/{run_id}/artifacts" + ], + listWorkflowRuns: [ + "GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs" + ], + listWorkflowRunsForRepo: ["GET /repos/{owner}/{repo}/actions/runs"], + reRunJobForWorkflowRun: [ + "POST /repos/{owner}/{repo}/actions/jobs/{job_id}/rerun" + ], + reRunWorkflow: ["POST /repos/{owner}/{repo}/actions/runs/{run_id}/rerun"], + reRunWorkflowFailedJobs: [ + "POST /repos/{owner}/{repo}/actions/runs/{run_id}/rerun-failed-jobs" + ], + removeAllCustomLabelsFromSelfHostedRunnerForOrg: [ + "DELETE /orgs/{org}/actions/runners/{runner_id}/labels" + ], + removeAllCustomLabelsFromSelfHostedRunnerForRepo: [ + "DELETE /repos/{owner}/{repo}/actions/runners/{runner_id}/labels" + ], + removeCustomLabelFromSelfHostedRunnerForOrg: [ + "DELETE /orgs/{org}/actions/runners/{runner_id}/labels/{name}" + ], + removeCustomLabelFromSelfHostedRunnerForRepo: [ + "DELETE /repos/{owner}/{repo}/actions/runners/{runner_id}/labels/{name}" + ], + removeSelectedRepoFromOrgSecret: [ + "DELETE /orgs/{org}/actions/secrets/{secret_name}/repositories/{repository_id}" + ], + removeSelectedRepoFromOrgVariable: [ + "DELETE /orgs/{org}/actions/variables/{name}/repositories/{repository_id}" + ], + reviewCustomGatesForRun: [ + "POST /repos/{owner}/{repo}/actions/runs/{run_id}/deployment_protection_rule" + ], + reviewPendingDeploymentsForRun: [ + "POST /repos/{owner}/{repo}/actions/runs/{run_id}/pending_deployments" + ], + setAllowedActionsOrganization: [ + "PUT /orgs/{org}/actions/permissions/selected-actions" + ], + setAllowedActionsRepository: [ + "PUT /repos/{owner}/{repo}/actions/permissions/selected-actions" + ], + setCustomLabelsForSelfHostedRunnerForOrg: [ + "PUT /orgs/{org}/actions/runners/{runner_id}/labels" + ], + setCustomLabelsForSelfHostedRunnerForRepo: [ + "PUT /repos/{owner}/{repo}/actions/runners/{runner_id}/labels" + ], + setGithubActionsDefaultWorkflowPermissionsOrganization: [ + "PUT /orgs/{org}/actions/permissions/workflow" + ], + setGithubActionsDefaultWorkflowPermissionsRepository: [ + "PUT /repos/{owner}/{repo}/actions/permissions/workflow" + ], + setGithubActionsPermissionsOrganization: [ + "PUT /orgs/{org}/actions/permissions" + ], + setGithubActionsPermissionsRepository: [ + "PUT /repos/{owner}/{repo}/actions/permissions" + ], + setSelectedReposForOrgSecret: [ + "PUT /orgs/{org}/actions/secrets/{secret_name}/repositories" + ], + setSelectedReposForOrgVariable: [ + "PUT /orgs/{org}/actions/variables/{name}/repositories" + ], + setSelectedRepositoriesEnabledGithubActionsOrganization: [ + "PUT /orgs/{org}/actions/permissions/repositories" + ], + setWorkflowAccessToRepository: [ + "PUT /repos/{owner}/{repo}/actions/permissions/access" + ], + updateEnvironmentVariable: [ + "PATCH /repositories/{repository_id}/environments/{environment_name}/variables/{name}" + ], + updateOrgVariable: ["PATCH /orgs/{org}/actions/variables/{name}"], + updateRepoVariable: [ + "PATCH /repos/{owner}/{repo}/actions/variables/{name}" + ] + }, + activity: { + checkRepoIsStarredByAuthenticatedUser: ["GET /user/starred/{owner}/{repo}"], + deleteRepoSubscription: ["DELETE /repos/{owner}/{repo}/subscription"], + deleteThreadSubscription: [ + "DELETE /notifications/threads/{thread_id}/subscription" + ], + getFeeds: ["GET /feeds"], + getRepoSubscription: ["GET /repos/{owner}/{repo}/subscription"], + getThread: ["GET /notifications/threads/{thread_id}"], + getThreadSubscriptionForAuthenticatedUser: [ + "GET /notifications/threads/{thread_id}/subscription" + ], + listEventsForAuthenticatedUser: ["GET /users/{username}/events"], + listNotificationsForAuthenticatedUser: ["GET /notifications"], + listOrgEventsForAuthenticatedUser: [ + "GET /users/{username}/events/orgs/{org}" + ], + listPublicEvents: ["GET /events"], + listPublicEventsForRepoNetwork: ["GET /networks/{owner}/{repo}/events"], + listPublicEventsForUser: ["GET /users/{username}/events/public"], + listPublicOrgEvents: ["GET /orgs/{org}/events"], + listReceivedEventsForUser: ["GET /users/{username}/received_events"], + listReceivedPublicEventsForUser: [ + "GET /users/{username}/received_events/public" + ], + listRepoEvents: ["GET /repos/{owner}/{repo}/events"], + listRepoNotificationsForAuthenticatedUser: [ + "GET /repos/{owner}/{repo}/notifications" + ], + listReposStarredByAuthenticatedUser: ["GET /user/starred"], + listReposStarredByUser: ["GET /users/{username}/starred"], + listReposWatchedByUser: ["GET /users/{username}/subscriptions"], + listStargazersForRepo: ["GET /repos/{owner}/{repo}/stargazers"], + listWatchedReposForAuthenticatedUser: ["GET /user/subscriptions"], + listWatchersForRepo: ["GET /repos/{owner}/{repo}/subscribers"], + markNotificationsAsRead: ["PUT /notifications"], + markRepoNotificationsAsRead: ["PUT /repos/{owner}/{repo}/notifications"], + markThreadAsRead: ["PATCH /notifications/threads/{thread_id}"], + setRepoSubscription: ["PUT /repos/{owner}/{repo}/subscription"], + setThreadSubscription: [ + "PUT /notifications/threads/{thread_id}/subscription" + ], + starRepoForAuthenticatedUser: ["PUT /user/starred/{owner}/{repo}"], + unstarRepoForAuthenticatedUser: ["DELETE /user/starred/{owner}/{repo}"] + }, + apps: { + addRepoToInstallation: [ + "PUT /user/installations/{installation_id}/repositories/{repository_id}", + {}, + { renamed: ["apps", "addRepoToInstallationForAuthenticatedUser"] } + ], + addRepoToInstallationForAuthenticatedUser: [ + "PUT /user/installations/{installation_id}/repositories/{repository_id}" + ], + checkToken: ["POST /applications/{client_id}/token"], + createFromManifest: ["POST /app-manifests/{code}/conversions"], + createInstallationAccessToken: [ + "POST /app/installations/{installation_id}/access_tokens" + ], + deleteAuthorization: ["DELETE /applications/{client_id}/grant"], + deleteInstallation: ["DELETE /app/installations/{installation_id}"], + deleteToken: ["DELETE /applications/{client_id}/token"], + getAuthenticated: ["GET /app"], + getBySlug: ["GET /apps/{app_slug}"], + getInstallation: ["GET /app/installations/{installation_id}"], + getOrgInstallation: ["GET /orgs/{org}/installation"], + getRepoInstallation: ["GET /repos/{owner}/{repo}/installation"], + getSubscriptionPlanForAccount: [ + "GET /marketplace_listing/accounts/{account_id}" + ], + getSubscriptionPlanForAccountStubbed: [ + "GET /marketplace_listing/stubbed/accounts/{account_id}" + ], + getUserInstallation: ["GET /users/{username}/installation"], + getWebhookConfigForApp: ["GET /app/hook/config"], + getWebhookDelivery: ["GET /app/hook/deliveries/{delivery_id}"], + listAccountsForPlan: ["GET /marketplace_listing/plans/{plan_id}/accounts"], + listAccountsForPlanStubbed: [ + "GET /marketplace_listing/stubbed/plans/{plan_id}/accounts" + ], + listInstallationReposForAuthenticatedUser: [ + "GET /user/installations/{installation_id}/repositories" + ], + listInstallationRequestsForAuthenticatedApp: [ + "GET /app/installation-requests" + ], + listInstallations: ["GET /app/installations"], + listInstallationsForAuthenticatedUser: ["GET /user/installations"], + listPlans: ["GET /marketplace_listing/plans"], + listPlansStubbed: ["GET /marketplace_listing/stubbed/plans"], + listReposAccessibleToInstallation: ["GET /installation/repositories"], + listSubscriptionsForAuthenticatedUser: ["GET /user/marketplace_purchases"], + listSubscriptionsForAuthenticatedUserStubbed: [ + "GET /user/marketplace_purchases/stubbed" + ], + listWebhookDeliveries: ["GET /app/hook/deliveries"], + redeliverWebhookDelivery: [ + "POST /app/hook/deliveries/{delivery_id}/attempts" + ], + removeRepoFromInstallation: [ + "DELETE /user/installations/{installation_id}/repositories/{repository_id}", + {}, + { renamed: ["apps", "removeRepoFromInstallationForAuthenticatedUser"] } + ], + removeRepoFromInstallationForAuthenticatedUser: [ + "DELETE /user/installations/{installation_id}/repositories/{repository_id}" + ], + resetToken: ["PATCH /applications/{client_id}/token"], + revokeInstallationAccessToken: ["DELETE /installation/token"], + scopeToken: ["POST /applications/{client_id}/token/scoped"], + suspendInstallation: ["PUT /app/installations/{installation_id}/suspended"], + unsuspendInstallation: [ + "DELETE /app/installations/{installation_id}/suspended" + ], + updateWebhookConfigForApp: ["PATCH /app/hook/config"] + }, + billing: { + getGithubActionsBillingOrg: ["GET /orgs/{org}/settings/billing/actions"], + getGithubActionsBillingUser: [ + "GET /users/{username}/settings/billing/actions" + ], + getGithubPackagesBillingOrg: ["GET /orgs/{org}/settings/billing/packages"], + getGithubPackagesBillingUser: [ + "GET /users/{username}/settings/billing/packages" + ], + getSharedStorageBillingOrg: [ + "GET /orgs/{org}/settings/billing/shared-storage" + ], + getSharedStorageBillingUser: [ + "GET /users/{username}/settings/billing/shared-storage" + ] + }, + checks: { + create: ["POST /repos/{owner}/{repo}/check-runs"], + createSuite: ["POST /repos/{owner}/{repo}/check-suites"], + get: ["GET /repos/{owner}/{repo}/check-runs/{check_run_id}"], + getSuite: ["GET /repos/{owner}/{repo}/check-suites/{check_suite_id}"], + listAnnotations: [ + "GET /repos/{owner}/{repo}/check-runs/{check_run_id}/annotations" + ], + listForRef: ["GET /repos/{owner}/{repo}/commits/{ref}/check-runs"], + listForSuite: [ + "GET /repos/{owner}/{repo}/check-suites/{check_suite_id}/check-runs" + ], + listSuitesForRef: ["GET /repos/{owner}/{repo}/commits/{ref}/check-suites"], + rerequestRun: [ + "POST /repos/{owner}/{repo}/check-runs/{check_run_id}/rerequest" + ], + rerequestSuite: [ + "POST /repos/{owner}/{repo}/check-suites/{check_suite_id}/rerequest" + ], + setSuitesPreferences: [ + "PATCH /repos/{owner}/{repo}/check-suites/preferences" + ], + update: ["PATCH /repos/{owner}/{repo}/check-runs/{check_run_id}"] + }, + codeScanning: { + deleteAnalysis: [ + "DELETE /repos/{owner}/{repo}/code-scanning/analyses/{analysis_id}{?confirm_delete}" + ], + getAlert: [ + "GET /repos/{owner}/{repo}/code-scanning/alerts/{alert_number}", + {}, + { renamedParameters: { alert_id: "alert_number" } } + ], + getAnalysis: [ + "GET /repos/{owner}/{repo}/code-scanning/analyses/{analysis_id}" + ], + getCodeqlDatabase: [ + "GET /repos/{owner}/{repo}/code-scanning/codeql/databases/{language}" + ], + getDefaultSetup: ["GET /repos/{owner}/{repo}/code-scanning/default-setup"], + getSarif: ["GET /repos/{owner}/{repo}/code-scanning/sarifs/{sarif_id}"], + listAlertInstances: [ + "GET /repos/{owner}/{repo}/code-scanning/alerts/{alert_number}/instances" + ], + listAlertsForOrg: ["GET /orgs/{org}/code-scanning/alerts"], + listAlertsForRepo: ["GET /repos/{owner}/{repo}/code-scanning/alerts"], + listAlertsInstances: [ + "GET /repos/{owner}/{repo}/code-scanning/alerts/{alert_number}/instances", + {}, + { renamed: ["codeScanning", "listAlertInstances"] } + ], + listCodeqlDatabases: [ + "GET /repos/{owner}/{repo}/code-scanning/codeql/databases" + ], + listRecentAnalyses: ["GET /repos/{owner}/{repo}/code-scanning/analyses"], + updateAlert: [ + "PATCH /repos/{owner}/{repo}/code-scanning/alerts/{alert_number}" + ], + updateDefaultSetup: [ + "PATCH /repos/{owner}/{repo}/code-scanning/default-setup" + ], + uploadSarif: ["POST /repos/{owner}/{repo}/code-scanning/sarifs"] + }, + codesOfConduct: { + getAllCodesOfConduct: ["GET /codes_of_conduct"], + getConductCode: ["GET /codes_of_conduct/{key}"] + }, + codespaces: { + addRepositoryForSecretForAuthenticatedUser: [ + "PUT /user/codespaces/secrets/{secret_name}/repositories/{repository_id}" + ], + addSelectedRepoToOrgSecret: [ + "PUT /orgs/{org}/codespaces/secrets/{secret_name}/repositories/{repository_id}" + ], + checkPermissionsForDevcontainer: [ + "GET /repos/{owner}/{repo}/codespaces/permissions_check" + ], + codespaceMachinesForAuthenticatedUser: [ + "GET /user/codespaces/{codespace_name}/machines" + ], + createForAuthenticatedUser: ["POST /user/codespaces"], + createOrUpdateOrgSecret: [ + "PUT /orgs/{org}/codespaces/secrets/{secret_name}" + ], + createOrUpdateRepoSecret: [ + "PUT /repos/{owner}/{repo}/codespaces/secrets/{secret_name}" + ], + createOrUpdateSecretForAuthenticatedUser: [ + "PUT /user/codespaces/secrets/{secret_name}" + ], + createWithPrForAuthenticatedUser: [ + "POST /repos/{owner}/{repo}/pulls/{pull_number}/codespaces" + ], + createWithRepoForAuthenticatedUser: [ + "POST /repos/{owner}/{repo}/codespaces" + ], + deleteForAuthenticatedUser: ["DELETE /user/codespaces/{codespace_name}"], + deleteFromOrganization: [ + "DELETE /orgs/{org}/members/{username}/codespaces/{codespace_name}" + ], + deleteOrgSecret: ["DELETE /orgs/{org}/codespaces/secrets/{secret_name}"], + deleteRepoSecret: [ + "DELETE /repos/{owner}/{repo}/codespaces/secrets/{secret_name}" + ], + deleteSecretForAuthenticatedUser: [ + "DELETE /user/codespaces/secrets/{secret_name}" + ], + exportForAuthenticatedUser: [ + "POST /user/codespaces/{codespace_name}/exports" + ], + getCodespacesForUserInOrg: [ + "GET /orgs/{org}/members/{username}/codespaces" + ], + getExportDetailsForAuthenticatedUser: [ + "GET /user/codespaces/{codespace_name}/exports/{export_id}" + ], + getForAuthenticatedUser: ["GET /user/codespaces/{codespace_name}"], + getOrgPublicKey: ["GET /orgs/{org}/codespaces/secrets/public-key"], + getOrgSecret: ["GET /orgs/{org}/codespaces/secrets/{secret_name}"], + getPublicKeyForAuthenticatedUser: [ + "GET /user/codespaces/secrets/public-key" + ], + getRepoPublicKey: [ + "GET /repos/{owner}/{repo}/codespaces/secrets/public-key" + ], + getRepoSecret: [ + "GET /repos/{owner}/{repo}/codespaces/secrets/{secret_name}" + ], + getSecretForAuthenticatedUser: [ + "GET /user/codespaces/secrets/{secret_name}" + ], + listDevcontainersInRepositoryForAuthenticatedUser: [ + "GET /repos/{owner}/{repo}/codespaces/devcontainers" + ], + listForAuthenticatedUser: ["GET /user/codespaces"], + listInOrganization: [ + "GET /orgs/{org}/codespaces", + {}, + { renamedParameters: { org_id: "org" } } + ], + listInRepositoryForAuthenticatedUser: [ + "GET /repos/{owner}/{repo}/codespaces" + ], + listOrgSecrets: ["GET /orgs/{org}/codespaces/secrets"], + listRepoSecrets: ["GET /repos/{owner}/{repo}/codespaces/secrets"], + listRepositoriesForSecretForAuthenticatedUser: [ + "GET /user/codespaces/secrets/{secret_name}/repositories" + ], + listSecretsForAuthenticatedUser: ["GET /user/codespaces/secrets"], + listSelectedReposForOrgSecret: [ + "GET /orgs/{org}/codespaces/secrets/{secret_name}/repositories" + ], + preFlightWithRepoForAuthenticatedUser: [ + "GET /repos/{owner}/{repo}/codespaces/new" + ], + publishForAuthenticatedUser: [ + "POST /user/codespaces/{codespace_name}/publish" + ], + removeRepositoryForSecretForAuthenticatedUser: [ + "DELETE /user/codespaces/secrets/{secret_name}/repositories/{repository_id}" + ], + removeSelectedRepoFromOrgSecret: [ + "DELETE /orgs/{org}/codespaces/secrets/{secret_name}/repositories/{repository_id}" + ], + repoMachinesForAuthenticatedUser: [ + "GET /repos/{owner}/{repo}/codespaces/machines" + ], + setRepositoriesForSecretForAuthenticatedUser: [ + "PUT /user/codespaces/secrets/{secret_name}/repositories" + ], + setSelectedReposForOrgSecret: [ + "PUT /orgs/{org}/codespaces/secrets/{secret_name}/repositories" + ], + startForAuthenticatedUser: ["POST /user/codespaces/{codespace_name}/start"], + stopForAuthenticatedUser: ["POST /user/codespaces/{codespace_name}/stop"], + stopInOrganization: [ + "POST /orgs/{org}/members/{username}/codespaces/{codespace_name}/stop" + ], + updateForAuthenticatedUser: ["PATCH /user/codespaces/{codespace_name}"] + }, + copilot: { + addCopilotForBusinessSeatsForTeams: [ + "POST /orgs/{org}/copilot/billing/selected_teams" + ], + addCopilotForBusinessSeatsForUsers: [ + "POST /orgs/{org}/copilot/billing/selected_users" + ], + cancelCopilotSeatAssignmentForTeams: [ + "DELETE /orgs/{org}/copilot/billing/selected_teams" + ], + cancelCopilotSeatAssignmentForUsers: [ + "DELETE /orgs/{org}/copilot/billing/selected_users" + ], + getCopilotOrganizationDetails: ["GET /orgs/{org}/copilot/billing"], + getCopilotSeatDetailsForUser: [ + "GET /orgs/{org}/members/{username}/copilot" + ], + listCopilotSeats: ["GET /orgs/{org}/copilot/billing/seats"] + }, + dependabot: { + addSelectedRepoToOrgSecret: [ + "PUT /orgs/{org}/dependabot/secrets/{secret_name}/repositories/{repository_id}" + ], + createOrUpdateOrgSecret: [ + "PUT /orgs/{org}/dependabot/secrets/{secret_name}" + ], + createOrUpdateRepoSecret: [ + "PUT /repos/{owner}/{repo}/dependabot/secrets/{secret_name}" + ], + deleteOrgSecret: ["DELETE /orgs/{org}/dependabot/secrets/{secret_name}"], + deleteRepoSecret: [ + "DELETE /repos/{owner}/{repo}/dependabot/secrets/{secret_name}" + ], + getAlert: ["GET /repos/{owner}/{repo}/dependabot/alerts/{alert_number}"], + getOrgPublicKey: ["GET /orgs/{org}/dependabot/secrets/public-key"], + getOrgSecret: ["GET /orgs/{org}/dependabot/secrets/{secret_name}"], + getRepoPublicKey: [ + "GET /repos/{owner}/{repo}/dependabot/secrets/public-key" + ], + getRepoSecret: [ + "GET /repos/{owner}/{repo}/dependabot/secrets/{secret_name}" + ], + listAlertsForEnterprise: [ + "GET /enterprises/{enterprise}/dependabot/alerts" + ], + listAlertsForOrg: ["GET /orgs/{org}/dependabot/alerts"], + listAlertsForRepo: ["GET /repos/{owner}/{repo}/dependabot/alerts"], + listOrgSecrets: ["GET /orgs/{org}/dependabot/secrets"], + listRepoSecrets: ["GET /repos/{owner}/{repo}/dependabot/secrets"], + listSelectedReposForOrgSecret: [ + "GET /orgs/{org}/dependabot/secrets/{secret_name}/repositories" + ], + removeSelectedRepoFromOrgSecret: [ + "DELETE /orgs/{org}/dependabot/secrets/{secret_name}/repositories/{repository_id}" + ], + setSelectedReposForOrgSecret: [ + "PUT /orgs/{org}/dependabot/secrets/{secret_name}/repositories" + ], + updateAlert: [ + "PATCH /repos/{owner}/{repo}/dependabot/alerts/{alert_number}" + ] + }, + dependencyGraph: { + createRepositorySnapshot: [ + "POST /repos/{owner}/{repo}/dependency-graph/snapshots" + ], + diffRange: [ + "GET /repos/{owner}/{repo}/dependency-graph/compare/{basehead}" + ], + exportSbom: ["GET /repos/{owner}/{repo}/dependency-graph/sbom"] + }, + emojis: { get: ["GET /emojis"] }, + gists: { + checkIsStarred: ["GET /gists/{gist_id}/star"], + create: ["POST /gists"], + createComment: ["POST /gists/{gist_id}/comments"], + delete: ["DELETE /gists/{gist_id}"], + deleteComment: ["DELETE /gists/{gist_id}/comments/{comment_id}"], + fork: ["POST /gists/{gist_id}/forks"], + get: ["GET /gists/{gist_id}"], + getComment: ["GET /gists/{gist_id}/comments/{comment_id}"], + getRevision: ["GET /gists/{gist_id}/{sha}"], + list: ["GET /gists"], + listComments: ["GET /gists/{gist_id}/comments"], + listCommits: ["GET /gists/{gist_id}/commits"], + listForUser: ["GET /users/{username}/gists"], + listForks: ["GET /gists/{gist_id}/forks"], + listPublic: ["GET /gists/public"], + listStarred: ["GET /gists/starred"], + star: ["PUT /gists/{gist_id}/star"], + unstar: ["DELETE /gists/{gist_id}/star"], + update: ["PATCH /gists/{gist_id}"], + updateComment: ["PATCH /gists/{gist_id}/comments/{comment_id}"] + }, + git: { + createBlob: ["POST /repos/{owner}/{repo}/git/blobs"], + createCommit: ["POST /repos/{owner}/{repo}/git/commits"], + createRef: ["POST /repos/{owner}/{repo}/git/refs"], + createTag: ["POST /repos/{owner}/{repo}/git/tags"], + createTree: ["POST /repos/{owner}/{repo}/git/trees"], + deleteRef: ["DELETE /repos/{owner}/{repo}/git/refs/{ref}"], + getBlob: ["GET /repos/{owner}/{repo}/git/blobs/{file_sha}"], + getCommit: ["GET /repos/{owner}/{repo}/git/commits/{commit_sha}"], + getRef: ["GET /repos/{owner}/{repo}/git/ref/{ref}"], + getTag: ["GET /repos/{owner}/{repo}/git/tags/{tag_sha}"], + getTree: ["GET /repos/{owner}/{repo}/git/trees/{tree_sha}"], + listMatchingRefs: ["GET /repos/{owner}/{repo}/git/matching-refs/{ref}"], + updateRef: ["PATCH /repos/{owner}/{repo}/git/refs/{ref}"] + }, + gitignore: { + getAllTemplates: ["GET /gitignore/templates"], + getTemplate: ["GET /gitignore/templates/{name}"] + }, + interactions: { + getRestrictionsForAuthenticatedUser: ["GET /user/interaction-limits"], + getRestrictionsForOrg: ["GET /orgs/{org}/interaction-limits"], + getRestrictionsForRepo: ["GET /repos/{owner}/{repo}/interaction-limits"], + getRestrictionsForYourPublicRepos: [ + "GET /user/interaction-limits", + {}, + { renamed: ["interactions", "getRestrictionsForAuthenticatedUser"] } + ], + removeRestrictionsForAuthenticatedUser: ["DELETE /user/interaction-limits"], + removeRestrictionsForOrg: ["DELETE /orgs/{org}/interaction-limits"], + removeRestrictionsForRepo: [ + "DELETE /repos/{owner}/{repo}/interaction-limits" + ], + removeRestrictionsForYourPublicRepos: [ + "DELETE /user/interaction-limits", + {}, + { renamed: ["interactions", "removeRestrictionsForAuthenticatedUser"] } + ], + setRestrictionsForAuthenticatedUser: ["PUT /user/interaction-limits"], + setRestrictionsForOrg: ["PUT /orgs/{org}/interaction-limits"], + setRestrictionsForRepo: ["PUT /repos/{owner}/{repo}/interaction-limits"], + setRestrictionsForYourPublicRepos: [ + "PUT /user/interaction-limits", + {}, + { renamed: ["interactions", "setRestrictionsForAuthenticatedUser"] } + ] + }, + issues: { + addAssignees: [ + "POST /repos/{owner}/{repo}/issues/{issue_number}/assignees" + ], + addLabels: ["POST /repos/{owner}/{repo}/issues/{issue_number}/labels"], + checkUserCanBeAssigned: ["GET /repos/{owner}/{repo}/assignees/{assignee}"], + checkUserCanBeAssignedToIssue: [ + "GET /repos/{owner}/{repo}/issues/{issue_number}/assignees/{assignee}" + ], + create: ["POST /repos/{owner}/{repo}/issues"], + createComment: [ + "POST /repos/{owner}/{repo}/issues/{issue_number}/comments" + ], + createLabel: ["POST /repos/{owner}/{repo}/labels"], + createMilestone: ["POST /repos/{owner}/{repo}/milestones"], + deleteComment: [ + "DELETE /repos/{owner}/{repo}/issues/comments/{comment_id}" + ], + deleteLabel: ["DELETE /repos/{owner}/{repo}/labels/{name}"], + deleteMilestone: [ + "DELETE /repos/{owner}/{repo}/milestones/{milestone_number}" + ], + get: ["GET /repos/{owner}/{repo}/issues/{issue_number}"], + getComment: ["GET /repos/{owner}/{repo}/issues/comments/{comment_id}"], + getEvent: ["GET /repos/{owner}/{repo}/issues/events/{event_id}"], + getLabel: ["GET /repos/{owner}/{repo}/labels/{name}"], + getMilestone: ["GET /repos/{owner}/{repo}/milestones/{milestone_number}"], + list: ["GET /issues"], + listAssignees: ["GET /repos/{owner}/{repo}/assignees"], + listComments: ["GET /repos/{owner}/{repo}/issues/{issue_number}/comments"], + listCommentsForRepo: ["GET /repos/{owner}/{repo}/issues/comments"], + listEvents: ["GET /repos/{owner}/{repo}/issues/{issue_number}/events"], + listEventsForRepo: ["GET /repos/{owner}/{repo}/issues/events"], + listEventsForTimeline: [ + "GET /repos/{owner}/{repo}/issues/{issue_number}/timeline" + ], + listForAuthenticatedUser: ["GET /user/issues"], + listForOrg: ["GET /orgs/{org}/issues"], + listForRepo: ["GET /repos/{owner}/{repo}/issues"], + listLabelsForMilestone: [ + "GET /repos/{owner}/{repo}/milestones/{milestone_number}/labels" + ], + listLabelsForRepo: ["GET /repos/{owner}/{repo}/labels"], + listLabelsOnIssue: [ + "GET /repos/{owner}/{repo}/issues/{issue_number}/labels" + ], + listMilestones: ["GET /repos/{owner}/{repo}/milestones"], + lock: ["PUT /repos/{owner}/{repo}/issues/{issue_number}/lock"], + removeAllLabels: [ + "DELETE /repos/{owner}/{repo}/issues/{issue_number}/labels" + ], + removeAssignees: [ + "DELETE /repos/{owner}/{repo}/issues/{issue_number}/assignees" + ], + removeLabel: [ + "DELETE /repos/{owner}/{repo}/issues/{issue_number}/labels/{name}" + ], + setLabels: ["PUT /repos/{owner}/{repo}/issues/{issue_number}/labels"], + unlock: ["DELETE /repos/{owner}/{repo}/issues/{issue_number}/lock"], + update: ["PATCH /repos/{owner}/{repo}/issues/{issue_number}"], + updateComment: ["PATCH /repos/{owner}/{repo}/issues/comments/{comment_id}"], + updateLabel: ["PATCH /repos/{owner}/{repo}/labels/{name}"], + updateMilestone: [ + "PATCH /repos/{owner}/{repo}/milestones/{milestone_number}" + ] + }, + licenses: { + get: ["GET /licenses/{license}"], + getAllCommonlyUsed: ["GET /licenses"], + getForRepo: ["GET /repos/{owner}/{repo}/license"] + }, + markdown: { + render: ["POST /markdown"], + renderRaw: [ + "POST /markdown/raw", + { headers: { "content-type": "text/plain; charset=utf-8" } } + ] + }, + meta: { + get: ["GET /meta"], + getAllVersions: ["GET /versions"], + getOctocat: ["GET /octocat"], + getZen: ["GET /zen"], + root: ["GET /"] + }, + migrations: { + cancelImport: [ + "DELETE /repos/{owner}/{repo}/import", + {}, + { + deprecated: "octokit.rest.migrations.cancelImport() is deprecated, see https://docs.github.com/rest/migrations/source-imports#cancel-an-import" + } + ], + deleteArchiveForAuthenticatedUser: [ + "DELETE /user/migrations/{migration_id}/archive" + ], + deleteArchiveForOrg: [ + "DELETE /orgs/{org}/migrations/{migration_id}/archive" + ], + downloadArchiveForOrg: [ + "GET /orgs/{org}/migrations/{migration_id}/archive" + ], + getArchiveForAuthenticatedUser: [ + "GET /user/migrations/{migration_id}/archive" + ], + getCommitAuthors: [ + "GET /repos/{owner}/{repo}/import/authors", + {}, + { + deprecated: "octokit.rest.migrations.getCommitAuthors() is deprecated, see https://docs.github.com/rest/migrations/source-imports#get-commit-authors" + } + ], + getImportStatus: [ + "GET /repos/{owner}/{repo}/import", + {}, + { + deprecated: "octokit.rest.migrations.getImportStatus() is deprecated, see https://docs.github.com/rest/migrations/source-imports#get-an-import-status" + } + ], + getLargeFiles: [ + "GET /repos/{owner}/{repo}/import/large_files", + {}, + { + deprecated: "octokit.rest.migrations.getLargeFiles() is deprecated, see https://docs.github.com/rest/migrations/source-imports#get-large-files" + } + ], + getStatusForAuthenticatedUser: ["GET /user/migrations/{migration_id}"], + getStatusForOrg: ["GET /orgs/{org}/migrations/{migration_id}"], + listForAuthenticatedUser: ["GET /user/migrations"], + listForOrg: ["GET /orgs/{org}/migrations"], + listReposForAuthenticatedUser: [ + "GET /user/migrations/{migration_id}/repositories" + ], + listReposForOrg: ["GET /orgs/{org}/migrations/{migration_id}/repositories"], + listReposForUser: [ + "GET /user/migrations/{migration_id}/repositories", + {}, + { renamed: ["migrations", "listReposForAuthenticatedUser"] } + ], + mapCommitAuthor: [ + "PATCH /repos/{owner}/{repo}/import/authors/{author_id}", + {}, + { + deprecated: "octokit.rest.migrations.mapCommitAuthor() is deprecated, see https://docs.github.com/rest/migrations/source-imports#map-a-commit-author" + } + ], + setLfsPreference: [ + "PATCH /repos/{owner}/{repo}/import/lfs", + {}, + { + deprecated: "octokit.rest.migrations.setLfsPreference() is deprecated, see https://docs.github.com/rest/migrations/source-imports#update-git-lfs-preference" + } + ], + startForAuthenticatedUser: ["POST /user/migrations"], + startForOrg: ["POST /orgs/{org}/migrations"], + startImport: [ + "PUT /repos/{owner}/{repo}/import", + {}, + { + deprecated: "octokit.rest.migrations.startImport() is deprecated, see https://docs.github.com/rest/migrations/source-imports#start-an-import" + } + ], + unlockRepoForAuthenticatedUser: [ + "DELETE /user/migrations/{migration_id}/repos/{repo_name}/lock" + ], + unlockRepoForOrg: [ + "DELETE /orgs/{org}/migrations/{migration_id}/repos/{repo_name}/lock" + ], + updateImport: [ + "PATCH /repos/{owner}/{repo}/import", + {}, + { + deprecated: "octokit.rest.migrations.updateImport() is deprecated, see https://docs.github.com/rest/migrations/source-imports#update-an-import" + } + ] + }, + orgs: { + addSecurityManagerTeam: [ + "PUT /orgs/{org}/security-managers/teams/{team_slug}" + ], + blockUser: ["PUT /orgs/{org}/blocks/{username}"], + cancelInvitation: ["DELETE /orgs/{org}/invitations/{invitation_id}"], + checkBlockedUser: ["GET /orgs/{org}/blocks/{username}"], + checkMembershipForUser: ["GET /orgs/{org}/members/{username}"], + checkPublicMembershipForUser: ["GET /orgs/{org}/public_members/{username}"], + convertMemberToOutsideCollaborator: [ + "PUT /orgs/{org}/outside_collaborators/{username}" + ], + createInvitation: ["POST /orgs/{org}/invitations"], + createOrUpdateCustomProperties: ["PATCH /orgs/{org}/properties/schema"], + createOrUpdateCustomPropertiesValuesForRepos: [ + "PATCH /orgs/{org}/properties/values" + ], + createOrUpdateCustomProperty: [ + "PUT /orgs/{org}/properties/schema/{custom_property_name}" + ], + createWebhook: ["POST /orgs/{org}/hooks"], + delete: ["DELETE /orgs/{org}"], + deleteWebhook: ["DELETE /orgs/{org}/hooks/{hook_id}"], + enableOrDisableSecurityProductOnAllOrgRepos: [ + "POST /orgs/{org}/{security_product}/{enablement}" + ], + get: ["GET /orgs/{org}"], + getAllCustomProperties: ["GET /orgs/{org}/properties/schema"], + getCustomProperty: [ + "GET /orgs/{org}/properties/schema/{custom_property_name}" + ], + getMembershipForAuthenticatedUser: ["GET /user/memberships/orgs/{org}"], + getMembershipForUser: ["GET /orgs/{org}/memberships/{username}"], + getWebhook: ["GET /orgs/{org}/hooks/{hook_id}"], + getWebhookConfigForOrg: ["GET /orgs/{org}/hooks/{hook_id}/config"], + getWebhookDelivery: [ + "GET /orgs/{org}/hooks/{hook_id}/deliveries/{delivery_id}" + ], + list: ["GET /organizations"], + listAppInstallations: ["GET /orgs/{org}/installations"], + listBlockedUsers: ["GET /orgs/{org}/blocks"], + listCustomPropertiesValuesForRepos: ["GET /orgs/{org}/properties/values"], + listFailedInvitations: ["GET /orgs/{org}/failed_invitations"], + listForAuthenticatedUser: ["GET /user/orgs"], + listForUser: ["GET /users/{username}/orgs"], + listInvitationTeams: ["GET /orgs/{org}/invitations/{invitation_id}/teams"], + listMembers: ["GET /orgs/{org}/members"], + listMembershipsForAuthenticatedUser: ["GET /user/memberships/orgs"], + listOutsideCollaborators: ["GET /orgs/{org}/outside_collaborators"], + listPatGrantRepositories: [ + "GET /orgs/{org}/personal-access-tokens/{pat_id}/repositories" + ], + listPatGrantRequestRepositories: [ + "GET /orgs/{org}/personal-access-token-requests/{pat_request_id}/repositories" + ], + listPatGrantRequests: ["GET /orgs/{org}/personal-access-token-requests"], + listPatGrants: ["GET /orgs/{org}/personal-access-tokens"], + listPendingInvitations: ["GET /orgs/{org}/invitations"], + listPublicMembers: ["GET /orgs/{org}/public_members"], + listSecurityManagerTeams: ["GET /orgs/{org}/security-managers"], + listWebhookDeliveries: ["GET /orgs/{org}/hooks/{hook_id}/deliveries"], + listWebhooks: ["GET /orgs/{org}/hooks"], + pingWebhook: ["POST /orgs/{org}/hooks/{hook_id}/pings"], + redeliverWebhookDelivery: [ + "POST /orgs/{org}/hooks/{hook_id}/deliveries/{delivery_id}/attempts" + ], + removeCustomProperty: [ + "DELETE /orgs/{org}/properties/schema/{custom_property_name}" + ], + removeMember: ["DELETE /orgs/{org}/members/{username}"], + removeMembershipForUser: ["DELETE /orgs/{org}/memberships/{username}"], + removeOutsideCollaborator: [ + "DELETE /orgs/{org}/outside_collaborators/{username}" + ], + removePublicMembershipForAuthenticatedUser: [ + "DELETE /orgs/{org}/public_members/{username}" + ], + removeSecurityManagerTeam: [ + "DELETE /orgs/{org}/security-managers/teams/{team_slug}" + ], + reviewPatGrantRequest: [ + "POST /orgs/{org}/personal-access-token-requests/{pat_request_id}" + ], + reviewPatGrantRequestsInBulk: [ + "POST /orgs/{org}/personal-access-token-requests" + ], + setMembershipForUser: ["PUT /orgs/{org}/memberships/{username}"], + setPublicMembershipForAuthenticatedUser: [ + "PUT /orgs/{org}/public_members/{username}" + ], + unblockUser: ["DELETE /orgs/{org}/blocks/{username}"], + update: ["PATCH /orgs/{org}"], + updateMembershipForAuthenticatedUser: [ + "PATCH /user/memberships/orgs/{org}" + ], + updatePatAccess: ["POST /orgs/{org}/personal-access-tokens/{pat_id}"], + updatePatAccesses: ["POST /orgs/{org}/personal-access-tokens"], + updateWebhook: ["PATCH /orgs/{org}/hooks/{hook_id}"], + updateWebhookConfigForOrg: ["PATCH /orgs/{org}/hooks/{hook_id}/config"] + }, + packages: { + deletePackageForAuthenticatedUser: [ + "DELETE /user/packages/{package_type}/{package_name}" + ], + deletePackageForOrg: [ + "DELETE /orgs/{org}/packages/{package_type}/{package_name}" + ], + deletePackageForUser: [ + "DELETE /users/{username}/packages/{package_type}/{package_name}" + ], + deletePackageVersionForAuthenticatedUser: [ + "DELETE /user/packages/{package_type}/{package_name}/versions/{package_version_id}" + ], + deletePackageVersionForOrg: [ + "DELETE /orgs/{org}/packages/{package_type}/{package_name}/versions/{package_version_id}" + ], + deletePackageVersionForUser: [ + "DELETE /users/{username}/packages/{package_type}/{package_name}/versions/{package_version_id}" + ], + getAllPackageVersionsForAPackageOwnedByAnOrg: [ + "GET /orgs/{org}/packages/{package_type}/{package_name}/versions", + {}, + { renamed: ["packages", "getAllPackageVersionsForPackageOwnedByOrg"] } + ], + getAllPackageVersionsForAPackageOwnedByTheAuthenticatedUser: [ + "GET /user/packages/{package_type}/{package_name}/versions", + {}, + { + renamed: [ + "packages", + "getAllPackageVersionsForPackageOwnedByAuthenticatedUser" + ] + } + ], + getAllPackageVersionsForPackageOwnedByAuthenticatedUser: [ + "GET /user/packages/{package_type}/{package_name}/versions" + ], + getAllPackageVersionsForPackageOwnedByOrg: [ + "GET /orgs/{org}/packages/{package_type}/{package_name}/versions" + ], + getAllPackageVersionsForPackageOwnedByUser: [ + "GET /users/{username}/packages/{package_type}/{package_name}/versions" + ], + getPackageForAuthenticatedUser: [ + "GET /user/packages/{package_type}/{package_name}" + ], + getPackageForOrganization: [ + "GET /orgs/{org}/packages/{package_type}/{package_name}" + ], + getPackageForUser: [ + "GET /users/{username}/packages/{package_type}/{package_name}" + ], + getPackageVersionForAuthenticatedUser: [ + "GET /user/packages/{package_type}/{package_name}/versions/{package_version_id}" + ], + getPackageVersionForOrganization: [ + "GET /orgs/{org}/packages/{package_type}/{package_name}/versions/{package_version_id}" + ], + getPackageVersionForUser: [ + "GET /users/{username}/packages/{package_type}/{package_name}/versions/{package_version_id}" + ], + listDockerMigrationConflictingPackagesForAuthenticatedUser: [ + "GET /user/docker/conflicts" + ], + listDockerMigrationConflictingPackagesForOrganization: [ + "GET /orgs/{org}/docker/conflicts" + ], + listDockerMigrationConflictingPackagesForUser: [ + "GET /users/{username}/docker/conflicts" + ], + listPackagesForAuthenticatedUser: ["GET /user/packages"], + listPackagesForOrganization: ["GET /orgs/{org}/packages"], + listPackagesForUser: ["GET /users/{username}/packages"], + restorePackageForAuthenticatedUser: [ + "POST /user/packages/{package_type}/{package_name}/restore{?token}" + ], + restorePackageForOrg: [ + "POST /orgs/{org}/packages/{package_type}/{package_name}/restore{?token}" + ], + restorePackageForUser: [ + "POST /users/{username}/packages/{package_type}/{package_name}/restore{?token}" + ], + restorePackageVersionForAuthenticatedUser: [ + "POST /user/packages/{package_type}/{package_name}/versions/{package_version_id}/restore" + ], + restorePackageVersionForOrg: [ + "POST /orgs/{org}/packages/{package_type}/{package_name}/versions/{package_version_id}/restore" + ], + restorePackageVersionForUser: [ + "POST /users/{username}/packages/{package_type}/{package_name}/versions/{package_version_id}/restore" + ] + }, + projects: { + addCollaborator: ["PUT /projects/{project_id}/collaborators/{username}"], + createCard: ["POST /projects/columns/{column_id}/cards"], + createColumn: ["POST /projects/{project_id}/columns"], + createForAuthenticatedUser: ["POST /user/projects"], + createForOrg: ["POST /orgs/{org}/projects"], + createForRepo: ["POST /repos/{owner}/{repo}/projects"], + delete: ["DELETE /projects/{project_id}"], + deleteCard: ["DELETE /projects/columns/cards/{card_id}"], + deleteColumn: ["DELETE /projects/columns/{column_id}"], + get: ["GET /projects/{project_id}"], + getCard: ["GET /projects/columns/cards/{card_id}"], + getColumn: ["GET /projects/columns/{column_id}"], + getPermissionForUser: [ + "GET /projects/{project_id}/collaborators/{username}/permission" + ], + listCards: ["GET /projects/columns/{column_id}/cards"], + listCollaborators: ["GET /projects/{project_id}/collaborators"], + listColumns: ["GET /projects/{project_id}/columns"], + listForOrg: ["GET /orgs/{org}/projects"], + listForRepo: ["GET /repos/{owner}/{repo}/projects"], + listForUser: ["GET /users/{username}/projects"], + moveCard: ["POST /projects/columns/cards/{card_id}/moves"], + moveColumn: ["POST /projects/columns/{column_id}/moves"], + removeCollaborator: [ + "DELETE /projects/{project_id}/collaborators/{username}" + ], + update: ["PATCH /projects/{project_id}"], + updateCard: ["PATCH /projects/columns/cards/{card_id}"], + updateColumn: ["PATCH /projects/columns/{column_id}"] + }, + pulls: { + checkIfMerged: ["GET /repos/{owner}/{repo}/pulls/{pull_number}/merge"], + create: ["POST /repos/{owner}/{repo}/pulls"], + createReplyForReviewComment: [ + "POST /repos/{owner}/{repo}/pulls/{pull_number}/comments/{comment_id}/replies" + ], + createReview: ["POST /repos/{owner}/{repo}/pulls/{pull_number}/reviews"], + createReviewComment: [ + "POST /repos/{owner}/{repo}/pulls/{pull_number}/comments" + ], + deletePendingReview: [ + "DELETE /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}" + ], + deleteReviewComment: [ + "DELETE /repos/{owner}/{repo}/pulls/comments/{comment_id}" + ], + dismissReview: [ + "PUT /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/dismissals" + ], + get: ["GET /repos/{owner}/{repo}/pulls/{pull_number}"], + getReview: [ + "GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}" + ], + getReviewComment: ["GET /repos/{owner}/{repo}/pulls/comments/{comment_id}"], + list: ["GET /repos/{owner}/{repo}/pulls"], + listCommentsForReview: [ + "GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/comments" + ], + listCommits: ["GET /repos/{owner}/{repo}/pulls/{pull_number}/commits"], + listFiles: ["GET /repos/{owner}/{repo}/pulls/{pull_number}/files"], + listRequestedReviewers: [ + "GET /repos/{owner}/{repo}/pulls/{pull_number}/requested_reviewers" + ], + listReviewComments: [ + "GET /repos/{owner}/{repo}/pulls/{pull_number}/comments" + ], + listReviewCommentsForRepo: ["GET /repos/{owner}/{repo}/pulls/comments"], + listReviews: ["GET /repos/{owner}/{repo}/pulls/{pull_number}/reviews"], + merge: ["PUT /repos/{owner}/{repo}/pulls/{pull_number}/merge"], + removeRequestedReviewers: [ + "DELETE /repos/{owner}/{repo}/pulls/{pull_number}/requested_reviewers" + ], + requestReviewers: [ + "POST /repos/{owner}/{repo}/pulls/{pull_number}/requested_reviewers" + ], + submitReview: [ + "POST /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}/events" + ], + update: ["PATCH /repos/{owner}/{repo}/pulls/{pull_number}"], + updateBranch: [ + "PUT /repos/{owner}/{repo}/pulls/{pull_number}/update-branch" + ], + updateReview: [ + "PUT /repos/{owner}/{repo}/pulls/{pull_number}/reviews/{review_id}" + ], + updateReviewComment: [ + "PATCH /repos/{owner}/{repo}/pulls/comments/{comment_id}" + ] + }, + rateLimit: { get: ["GET /rate_limit"] }, + reactions: { + createForCommitComment: [ + "POST /repos/{owner}/{repo}/comments/{comment_id}/reactions" + ], + createForIssue: [ + "POST /repos/{owner}/{repo}/issues/{issue_number}/reactions" + ], + createForIssueComment: [ + "POST /repos/{owner}/{repo}/issues/comments/{comment_id}/reactions" + ], + createForPullRequestReviewComment: [ + "POST /repos/{owner}/{repo}/pulls/comments/{comment_id}/reactions" + ], + createForRelease: [ + "POST /repos/{owner}/{repo}/releases/{release_id}/reactions" + ], + createForTeamDiscussionCommentInOrg: [ + "POST /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}/reactions" + ], + createForTeamDiscussionInOrg: [ + "POST /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/reactions" + ], + deleteForCommitComment: [ + "DELETE /repos/{owner}/{repo}/comments/{comment_id}/reactions/{reaction_id}" + ], + deleteForIssue: [ + "DELETE /repos/{owner}/{repo}/issues/{issue_number}/reactions/{reaction_id}" + ], + deleteForIssueComment: [ + "DELETE /repos/{owner}/{repo}/issues/comments/{comment_id}/reactions/{reaction_id}" + ], + deleteForPullRequestComment: [ + "DELETE /repos/{owner}/{repo}/pulls/comments/{comment_id}/reactions/{reaction_id}" + ], + deleteForRelease: [ + "DELETE /repos/{owner}/{repo}/releases/{release_id}/reactions/{reaction_id}" + ], + deleteForTeamDiscussion: [ + "DELETE /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/reactions/{reaction_id}" + ], + deleteForTeamDiscussionComment: [ + "DELETE /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}/reactions/{reaction_id}" + ], + listForCommitComment: [ + "GET /repos/{owner}/{repo}/comments/{comment_id}/reactions" + ], + listForIssue: ["GET /repos/{owner}/{repo}/issues/{issue_number}/reactions"], + listForIssueComment: [ + "GET /repos/{owner}/{repo}/issues/comments/{comment_id}/reactions" + ], + listForPullRequestReviewComment: [ + "GET /repos/{owner}/{repo}/pulls/comments/{comment_id}/reactions" + ], + listForRelease: [ + "GET /repos/{owner}/{repo}/releases/{release_id}/reactions" + ], + listForTeamDiscussionCommentInOrg: [ + "GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}/reactions" + ], + listForTeamDiscussionInOrg: [ + "GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/reactions" + ] + }, + repos: { + acceptInvitation: [ + "PATCH /user/repository_invitations/{invitation_id}", + {}, + { renamed: ["repos", "acceptInvitationForAuthenticatedUser"] } + ], + acceptInvitationForAuthenticatedUser: [ + "PATCH /user/repository_invitations/{invitation_id}" + ], + addAppAccessRestrictions: [ + "POST /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/apps", + {}, + { mapToData: "apps" } + ], + addCollaborator: ["PUT /repos/{owner}/{repo}/collaborators/{username}"], + addStatusCheckContexts: [ + "POST /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks/contexts", + {}, + { mapToData: "contexts" } + ], + addTeamAccessRestrictions: [ + "POST /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams", + {}, + { mapToData: "teams" } + ], + addUserAccessRestrictions: [ + "POST /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users", + {}, + { mapToData: "users" } + ], + checkAutomatedSecurityFixes: [ + "GET /repos/{owner}/{repo}/automated-security-fixes" + ], + checkCollaborator: ["GET /repos/{owner}/{repo}/collaborators/{username}"], + checkVulnerabilityAlerts: [ + "GET /repos/{owner}/{repo}/vulnerability-alerts" + ], + codeownersErrors: ["GET /repos/{owner}/{repo}/codeowners/errors"], + compareCommits: ["GET /repos/{owner}/{repo}/compare/{base}...{head}"], + compareCommitsWithBasehead: [ + "GET /repos/{owner}/{repo}/compare/{basehead}" + ], + createAutolink: ["POST /repos/{owner}/{repo}/autolinks"], + createCommitComment: [ + "POST /repos/{owner}/{repo}/commits/{commit_sha}/comments" + ], + createCommitSignatureProtection: [ + "POST /repos/{owner}/{repo}/branches/{branch}/protection/required_signatures" + ], + createCommitStatus: ["POST /repos/{owner}/{repo}/statuses/{sha}"], + createDeployKey: ["POST /repos/{owner}/{repo}/keys"], + createDeployment: ["POST /repos/{owner}/{repo}/deployments"], + createDeploymentBranchPolicy: [ + "POST /repos/{owner}/{repo}/environments/{environment_name}/deployment-branch-policies" + ], + createDeploymentProtectionRule: [ + "POST /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules" + ], + createDeploymentStatus: [ + "POST /repos/{owner}/{repo}/deployments/{deployment_id}/statuses" + ], + createDispatchEvent: ["POST /repos/{owner}/{repo}/dispatches"], + createForAuthenticatedUser: ["POST /user/repos"], + createFork: ["POST /repos/{owner}/{repo}/forks"], + createInOrg: ["POST /orgs/{org}/repos"], + createOrUpdateEnvironment: [ + "PUT /repos/{owner}/{repo}/environments/{environment_name}" + ], + createOrUpdateFileContents: ["PUT /repos/{owner}/{repo}/contents/{path}"], + createOrgRuleset: ["POST /orgs/{org}/rulesets"], + createPagesDeployment: ["POST /repos/{owner}/{repo}/pages/deployment"], + createPagesSite: ["POST /repos/{owner}/{repo}/pages"], + createRelease: ["POST /repos/{owner}/{repo}/releases"], + createRepoRuleset: ["POST /repos/{owner}/{repo}/rulesets"], + createTagProtection: ["POST /repos/{owner}/{repo}/tags/protection"], + createUsingTemplate: [ + "POST /repos/{template_owner}/{template_repo}/generate" + ], + createWebhook: ["POST /repos/{owner}/{repo}/hooks"], + declineInvitation: [ + "DELETE /user/repository_invitations/{invitation_id}", + {}, + { renamed: ["repos", "declineInvitationForAuthenticatedUser"] } + ], + declineInvitationForAuthenticatedUser: [ + "DELETE /user/repository_invitations/{invitation_id}" + ], + delete: ["DELETE /repos/{owner}/{repo}"], + deleteAccessRestrictions: [ + "DELETE /repos/{owner}/{repo}/branches/{branch}/protection/restrictions" + ], + deleteAdminBranchProtection: [ + "DELETE /repos/{owner}/{repo}/branches/{branch}/protection/enforce_admins" + ], + deleteAnEnvironment: [ + "DELETE /repos/{owner}/{repo}/environments/{environment_name}" + ], + deleteAutolink: ["DELETE /repos/{owner}/{repo}/autolinks/{autolink_id}"], + deleteBranchProtection: [ + "DELETE /repos/{owner}/{repo}/branches/{branch}/protection" + ], + deleteCommitComment: ["DELETE /repos/{owner}/{repo}/comments/{comment_id}"], + deleteCommitSignatureProtection: [ + "DELETE /repos/{owner}/{repo}/branches/{branch}/protection/required_signatures" + ], + deleteDeployKey: ["DELETE /repos/{owner}/{repo}/keys/{key_id}"], + deleteDeployment: [ + "DELETE /repos/{owner}/{repo}/deployments/{deployment_id}" + ], + deleteDeploymentBranchPolicy: [ + "DELETE /repos/{owner}/{repo}/environments/{environment_name}/deployment-branch-policies/{branch_policy_id}" + ], + deleteFile: ["DELETE /repos/{owner}/{repo}/contents/{path}"], + deleteInvitation: [ + "DELETE /repos/{owner}/{repo}/invitations/{invitation_id}" + ], + deleteOrgRuleset: ["DELETE /orgs/{org}/rulesets/{ruleset_id}"], + deletePagesSite: ["DELETE /repos/{owner}/{repo}/pages"], + deletePullRequestReviewProtection: [ + "DELETE /repos/{owner}/{repo}/branches/{branch}/protection/required_pull_request_reviews" + ], + deleteRelease: ["DELETE /repos/{owner}/{repo}/releases/{release_id}"], + deleteReleaseAsset: [ + "DELETE /repos/{owner}/{repo}/releases/assets/{asset_id}" + ], + deleteRepoRuleset: ["DELETE /repos/{owner}/{repo}/rulesets/{ruleset_id}"], + deleteTagProtection: [ + "DELETE /repos/{owner}/{repo}/tags/protection/{tag_protection_id}" + ], + deleteWebhook: ["DELETE /repos/{owner}/{repo}/hooks/{hook_id}"], + disableAutomatedSecurityFixes: [ + "DELETE /repos/{owner}/{repo}/automated-security-fixes" + ], + disableDeploymentProtectionRule: [ + "DELETE /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules/{protection_rule_id}" + ], + disablePrivateVulnerabilityReporting: [ + "DELETE /repos/{owner}/{repo}/private-vulnerability-reporting" + ], + disableVulnerabilityAlerts: [ + "DELETE /repos/{owner}/{repo}/vulnerability-alerts" + ], + downloadArchive: [ + "GET /repos/{owner}/{repo}/zipball/{ref}", + {}, + { renamed: ["repos", "downloadZipballArchive"] } + ], + downloadTarballArchive: ["GET /repos/{owner}/{repo}/tarball/{ref}"], + downloadZipballArchive: ["GET /repos/{owner}/{repo}/zipball/{ref}"], + enableAutomatedSecurityFixes: [ + "PUT /repos/{owner}/{repo}/automated-security-fixes" + ], + enablePrivateVulnerabilityReporting: [ + "PUT /repos/{owner}/{repo}/private-vulnerability-reporting" + ], + enableVulnerabilityAlerts: [ + "PUT /repos/{owner}/{repo}/vulnerability-alerts" + ], + generateReleaseNotes: [ + "POST /repos/{owner}/{repo}/releases/generate-notes" + ], + get: ["GET /repos/{owner}/{repo}"], + getAccessRestrictions: [ + "GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions" + ], + getAdminBranchProtection: [ + "GET /repos/{owner}/{repo}/branches/{branch}/protection/enforce_admins" + ], + getAllDeploymentProtectionRules: [ + "GET /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules" + ], + getAllEnvironments: ["GET /repos/{owner}/{repo}/environments"], + getAllStatusCheckContexts: [ + "GET /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks/contexts" + ], + getAllTopics: ["GET /repos/{owner}/{repo}/topics"], + getAppsWithAccessToProtectedBranch: [ + "GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/apps" + ], + getAutolink: ["GET /repos/{owner}/{repo}/autolinks/{autolink_id}"], + getBranch: ["GET /repos/{owner}/{repo}/branches/{branch}"], + getBranchProtection: [ + "GET /repos/{owner}/{repo}/branches/{branch}/protection" + ], + getBranchRules: ["GET /repos/{owner}/{repo}/rules/branches/{branch}"], + getClones: ["GET /repos/{owner}/{repo}/traffic/clones"], + getCodeFrequencyStats: ["GET /repos/{owner}/{repo}/stats/code_frequency"], + getCollaboratorPermissionLevel: [ + "GET /repos/{owner}/{repo}/collaborators/{username}/permission" + ], + getCombinedStatusForRef: ["GET /repos/{owner}/{repo}/commits/{ref}/status"], + getCommit: ["GET /repos/{owner}/{repo}/commits/{ref}"], + getCommitActivityStats: ["GET /repos/{owner}/{repo}/stats/commit_activity"], + getCommitComment: ["GET /repos/{owner}/{repo}/comments/{comment_id}"], + getCommitSignatureProtection: [ + "GET /repos/{owner}/{repo}/branches/{branch}/protection/required_signatures" + ], + getCommunityProfileMetrics: ["GET /repos/{owner}/{repo}/community/profile"], + getContent: ["GET /repos/{owner}/{repo}/contents/{path}"], + getContributorsStats: ["GET /repos/{owner}/{repo}/stats/contributors"], + getCustomDeploymentProtectionRule: [ + "GET /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules/{protection_rule_id}" + ], + getCustomPropertiesValues: ["GET /repos/{owner}/{repo}/properties/values"], + getDeployKey: ["GET /repos/{owner}/{repo}/keys/{key_id}"], + getDeployment: ["GET /repos/{owner}/{repo}/deployments/{deployment_id}"], + getDeploymentBranchPolicy: [ + "GET /repos/{owner}/{repo}/environments/{environment_name}/deployment-branch-policies/{branch_policy_id}" + ], + getDeploymentStatus: [ + "GET /repos/{owner}/{repo}/deployments/{deployment_id}/statuses/{status_id}" + ], + getEnvironment: [ + "GET /repos/{owner}/{repo}/environments/{environment_name}" + ], + getLatestPagesBuild: ["GET /repos/{owner}/{repo}/pages/builds/latest"], + getLatestRelease: ["GET /repos/{owner}/{repo}/releases/latest"], + getOrgRuleSuite: ["GET /orgs/{org}/rulesets/rule-suites/{rule_suite_id}"], + getOrgRuleSuites: ["GET /orgs/{org}/rulesets/rule-suites"], + getOrgRuleset: ["GET /orgs/{org}/rulesets/{ruleset_id}"], + getOrgRulesets: ["GET /orgs/{org}/rulesets"], + getPages: ["GET /repos/{owner}/{repo}/pages"], + getPagesBuild: ["GET /repos/{owner}/{repo}/pages/builds/{build_id}"], + getPagesHealthCheck: ["GET /repos/{owner}/{repo}/pages/health"], + getParticipationStats: ["GET /repos/{owner}/{repo}/stats/participation"], + getPullRequestReviewProtection: [ + "GET /repos/{owner}/{repo}/branches/{branch}/protection/required_pull_request_reviews" + ], + getPunchCardStats: ["GET /repos/{owner}/{repo}/stats/punch_card"], + getReadme: ["GET /repos/{owner}/{repo}/readme"], + getReadmeInDirectory: ["GET /repos/{owner}/{repo}/readme/{dir}"], + getRelease: ["GET /repos/{owner}/{repo}/releases/{release_id}"], + getReleaseAsset: ["GET /repos/{owner}/{repo}/releases/assets/{asset_id}"], + getReleaseByTag: ["GET /repos/{owner}/{repo}/releases/tags/{tag}"], + getRepoRuleSuite: [ + "GET /repos/{owner}/{repo}/rulesets/rule-suites/{rule_suite_id}" + ], + getRepoRuleSuites: ["GET /repos/{owner}/{repo}/rulesets/rule-suites"], + getRepoRuleset: ["GET /repos/{owner}/{repo}/rulesets/{ruleset_id}"], + getRepoRulesets: ["GET /repos/{owner}/{repo}/rulesets"], + getStatusChecksProtection: [ + "GET /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks" + ], + getTeamsWithAccessToProtectedBranch: [ + "GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams" + ], + getTopPaths: ["GET /repos/{owner}/{repo}/traffic/popular/paths"], + getTopReferrers: ["GET /repos/{owner}/{repo}/traffic/popular/referrers"], + getUsersWithAccessToProtectedBranch: [ + "GET /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users" + ], + getViews: ["GET /repos/{owner}/{repo}/traffic/views"], + getWebhook: ["GET /repos/{owner}/{repo}/hooks/{hook_id}"], + getWebhookConfigForRepo: [ + "GET /repos/{owner}/{repo}/hooks/{hook_id}/config" + ], + getWebhookDelivery: [ + "GET /repos/{owner}/{repo}/hooks/{hook_id}/deliveries/{delivery_id}" + ], + listActivities: ["GET /repos/{owner}/{repo}/activity"], + listAutolinks: ["GET /repos/{owner}/{repo}/autolinks"], + listBranches: ["GET /repos/{owner}/{repo}/branches"], + listBranchesForHeadCommit: [ + "GET /repos/{owner}/{repo}/commits/{commit_sha}/branches-where-head" + ], + listCollaborators: ["GET /repos/{owner}/{repo}/collaborators"], + listCommentsForCommit: [ + "GET /repos/{owner}/{repo}/commits/{commit_sha}/comments" + ], + listCommitCommentsForRepo: ["GET /repos/{owner}/{repo}/comments"], + listCommitStatusesForRef: [ + "GET /repos/{owner}/{repo}/commits/{ref}/statuses" + ], + listCommits: ["GET /repos/{owner}/{repo}/commits"], + listContributors: ["GET /repos/{owner}/{repo}/contributors"], + listCustomDeploymentRuleIntegrations: [ + "GET /repos/{owner}/{repo}/environments/{environment_name}/deployment_protection_rules/apps" + ], + listDeployKeys: ["GET /repos/{owner}/{repo}/keys"], + listDeploymentBranchPolicies: [ + "GET /repos/{owner}/{repo}/environments/{environment_name}/deployment-branch-policies" + ], + listDeploymentStatuses: [ + "GET /repos/{owner}/{repo}/deployments/{deployment_id}/statuses" + ], + listDeployments: ["GET /repos/{owner}/{repo}/deployments"], + listForAuthenticatedUser: ["GET /user/repos"], + listForOrg: ["GET /orgs/{org}/repos"], + listForUser: ["GET /users/{username}/repos"], + listForks: ["GET /repos/{owner}/{repo}/forks"], + listInvitations: ["GET /repos/{owner}/{repo}/invitations"], + listInvitationsForAuthenticatedUser: ["GET /user/repository_invitations"], + listLanguages: ["GET /repos/{owner}/{repo}/languages"], + listPagesBuilds: ["GET /repos/{owner}/{repo}/pages/builds"], + listPublic: ["GET /repositories"], + listPullRequestsAssociatedWithCommit: [ + "GET /repos/{owner}/{repo}/commits/{commit_sha}/pulls" + ], + listReleaseAssets: [ + "GET /repos/{owner}/{repo}/releases/{release_id}/assets" + ], + listReleases: ["GET /repos/{owner}/{repo}/releases"], + listTagProtection: ["GET /repos/{owner}/{repo}/tags/protection"], + listTags: ["GET /repos/{owner}/{repo}/tags"], + listTeams: ["GET /repos/{owner}/{repo}/teams"], + listWebhookDeliveries: [ + "GET /repos/{owner}/{repo}/hooks/{hook_id}/deliveries" + ], + listWebhooks: ["GET /repos/{owner}/{repo}/hooks"], + merge: ["POST /repos/{owner}/{repo}/merges"], + mergeUpstream: ["POST /repos/{owner}/{repo}/merge-upstream"], + pingWebhook: ["POST /repos/{owner}/{repo}/hooks/{hook_id}/pings"], + redeliverWebhookDelivery: [ + "POST /repos/{owner}/{repo}/hooks/{hook_id}/deliveries/{delivery_id}/attempts" + ], + removeAppAccessRestrictions: [ + "DELETE /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/apps", + {}, + { mapToData: "apps" } + ], + removeCollaborator: [ + "DELETE /repos/{owner}/{repo}/collaborators/{username}" + ], + removeStatusCheckContexts: [ + "DELETE /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks/contexts", + {}, + { mapToData: "contexts" } + ], + removeStatusCheckProtection: [ + "DELETE /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks" + ], + removeTeamAccessRestrictions: [ + "DELETE /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams", + {}, + { mapToData: "teams" } + ], + removeUserAccessRestrictions: [ + "DELETE /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users", + {}, + { mapToData: "users" } + ], + renameBranch: ["POST /repos/{owner}/{repo}/branches/{branch}/rename"], + replaceAllTopics: ["PUT /repos/{owner}/{repo}/topics"], + requestPagesBuild: ["POST /repos/{owner}/{repo}/pages/builds"], + setAdminBranchProtection: [ + "POST /repos/{owner}/{repo}/branches/{branch}/protection/enforce_admins" + ], + setAppAccessRestrictions: [ + "PUT /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/apps", + {}, + { mapToData: "apps" } + ], + setStatusCheckContexts: [ + "PUT /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks/contexts", + {}, + { mapToData: "contexts" } + ], + setTeamAccessRestrictions: [ + "PUT /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/teams", + {}, + { mapToData: "teams" } + ], + setUserAccessRestrictions: [ + "PUT /repos/{owner}/{repo}/branches/{branch}/protection/restrictions/users", + {}, + { mapToData: "users" } + ], + testPushWebhook: ["POST /repos/{owner}/{repo}/hooks/{hook_id}/tests"], + transfer: ["POST /repos/{owner}/{repo}/transfer"], + update: ["PATCH /repos/{owner}/{repo}"], + updateBranchProtection: [ + "PUT /repos/{owner}/{repo}/branches/{branch}/protection" + ], + updateCommitComment: ["PATCH /repos/{owner}/{repo}/comments/{comment_id}"], + updateDeploymentBranchPolicy: [ + "PUT /repos/{owner}/{repo}/environments/{environment_name}/deployment-branch-policies/{branch_policy_id}" + ], + updateInformationAboutPagesSite: ["PUT /repos/{owner}/{repo}/pages"], + updateInvitation: [ + "PATCH /repos/{owner}/{repo}/invitations/{invitation_id}" + ], + updateOrgRuleset: ["PUT /orgs/{org}/rulesets/{ruleset_id}"], + updatePullRequestReviewProtection: [ + "PATCH /repos/{owner}/{repo}/branches/{branch}/protection/required_pull_request_reviews" + ], + updateRelease: ["PATCH /repos/{owner}/{repo}/releases/{release_id}"], + updateReleaseAsset: [ + "PATCH /repos/{owner}/{repo}/releases/assets/{asset_id}" + ], + updateRepoRuleset: ["PUT /repos/{owner}/{repo}/rulesets/{ruleset_id}"], + updateStatusCheckPotection: [ + "PATCH /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks", + {}, + { renamed: ["repos", "updateStatusCheckProtection"] } + ], + updateStatusCheckProtection: [ + "PATCH /repos/{owner}/{repo}/branches/{branch}/protection/required_status_checks" + ], + updateWebhook: ["PATCH /repos/{owner}/{repo}/hooks/{hook_id}"], + updateWebhookConfigForRepo: [ + "PATCH /repos/{owner}/{repo}/hooks/{hook_id}/config" + ], + uploadReleaseAsset: [ + "POST /repos/{owner}/{repo}/releases/{release_id}/assets{?name,label}", + { baseUrl: "https://uploads.github.com" } + ] + }, + search: { + code: ["GET /search/code"], + commits: ["GET /search/commits"], + issuesAndPullRequests: ["GET /search/issues"], + labels: ["GET /search/labels"], + repos: ["GET /search/repositories"], + topics: ["GET /search/topics"], + users: ["GET /search/users"] + }, + secretScanning: { + getAlert: [ + "GET /repos/{owner}/{repo}/secret-scanning/alerts/{alert_number}" + ], + listAlertsForEnterprise: [ + "GET /enterprises/{enterprise}/secret-scanning/alerts" + ], + listAlertsForOrg: ["GET /orgs/{org}/secret-scanning/alerts"], + listAlertsForRepo: ["GET /repos/{owner}/{repo}/secret-scanning/alerts"], + listLocationsForAlert: [ + "GET /repos/{owner}/{repo}/secret-scanning/alerts/{alert_number}/locations" + ], + updateAlert: [ + "PATCH /repos/{owner}/{repo}/secret-scanning/alerts/{alert_number}" + ] + }, + securityAdvisories: { + createPrivateVulnerabilityReport: [ + "POST /repos/{owner}/{repo}/security-advisories/reports" + ], + createRepositoryAdvisory: [ + "POST /repos/{owner}/{repo}/security-advisories" + ], + createRepositoryAdvisoryCveRequest: [ + "POST /repos/{owner}/{repo}/security-advisories/{ghsa_id}/cve" + ], + getGlobalAdvisory: ["GET /advisories/{ghsa_id}"], + getRepositoryAdvisory: [ + "GET /repos/{owner}/{repo}/security-advisories/{ghsa_id}" + ], + listGlobalAdvisories: ["GET /advisories"], + listOrgRepositoryAdvisories: ["GET /orgs/{org}/security-advisories"], + listRepositoryAdvisories: ["GET /repos/{owner}/{repo}/security-advisories"], + updateRepositoryAdvisory: [ + "PATCH /repos/{owner}/{repo}/security-advisories/{ghsa_id}" + ] + }, + teams: { + addOrUpdateMembershipForUserInOrg: [ + "PUT /orgs/{org}/teams/{team_slug}/memberships/{username}" + ], + addOrUpdateProjectPermissionsInOrg: [ + "PUT /orgs/{org}/teams/{team_slug}/projects/{project_id}" + ], + addOrUpdateRepoPermissionsInOrg: [ + "PUT /orgs/{org}/teams/{team_slug}/repos/{owner}/{repo}" + ], + checkPermissionsForProjectInOrg: [ + "GET /orgs/{org}/teams/{team_slug}/projects/{project_id}" + ], + checkPermissionsForRepoInOrg: [ + "GET /orgs/{org}/teams/{team_slug}/repos/{owner}/{repo}" + ], + create: ["POST /orgs/{org}/teams"], + createDiscussionCommentInOrg: [ + "POST /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments" + ], + createDiscussionInOrg: ["POST /orgs/{org}/teams/{team_slug}/discussions"], + deleteDiscussionCommentInOrg: [ + "DELETE /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}" + ], + deleteDiscussionInOrg: [ + "DELETE /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}" + ], + deleteInOrg: ["DELETE /orgs/{org}/teams/{team_slug}"], + getByName: ["GET /orgs/{org}/teams/{team_slug}"], + getDiscussionCommentInOrg: [ + "GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}" + ], + getDiscussionInOrg: [ + "GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}" + ], + getMembershipForUserInOrg: [ + "GET /orgs/{org}/teams/{team_slug}/memberships/{username}" + ], + list: ["GET /orgs/{org}/teams"], + listChildInOrg: ["GET /orgs/{org}/teams/{team_slug}/teams"], + listDiscussionCommentsInOrg: [ + "GET /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments" + ], + listDiscussionsInOrg: ["GET /orgs/{org}/teams/{team_slug}/discussions"], + listForAuthenticatedUser: ["GET /user/teams"], + listMembersInOrg: ["GET /orgs/{org}/teams/{team_slug}/members"], + listPendingInvitationsInOrg: [ + "GET /orgs/{org}/teams/{team_slug}/invitations" + ], + listProjectsInOrg: ["GET /orgs/{org}/teams/{team_slug}/projects"], + listReposInOrg: ["GET /orgs/{org}/teams/{team_slug}/repos"], + removeMembershipForUserInOrg: [ + "DELETE /orgs/{org}/teams/{team_slug}/memberships/{username}" + ], + removeProjectInOrg: [ + "DELETE /orgs/{org}/teams/{team_slug}/projects/{project_id}" + ], + removeRepoInOrg: [ + "DELETE /orgs/{org}/teams/{team_slug}/repos/{owner}/{repo}" + ], + updateDiscussionCommentInOrg: [ + "PATCH /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments/{comment_number}" + ], + updateDiscussionInOrg: [ + "PATCH /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}" + ], + updateInOrg: ["PATCH /orgs/{org}/teams/{team_slug}"] + }, + users: { + addEmailForAuthenticated: [ + "POST /user/emails", + {}, + { renamed: ["users", "addEmailForAuthenticatedUser"] } + ], + addEmailForAuthenticatedUser: ["POST /user/emails"], + addSocialAccountForAuthenticatedUser: ["POST /user/social_accounts"], + block: ["PUT /user/blocks/{username}"], + checkBlocked: ["GET /user/blocks/{username}"], + checkFollowingForUser: ["GET /users/{username}/following/{target_user}"], + checkPersonIsFollowedByAuthenticated: ["GET /user/following/{username}"], + createGpgKeyForAuthenticated: [ + "POST /user/gpg_keys", + {}, + { renamed: ["users", "createGpgKeyForAuthenticatedUser"] } + ], + createGpgKeyForAuthenticatedUser: ["POST /user/gpg_keys"], + createPublicSshKeyForAuthenticated: [ + "POST /user/keys", + {}, + { renamed: ["users", "createPublicSshKeyForAuthenticatedUser"] } + ], + createPublicSshKeyForAuthenticatedUser: ["POST /user/keys"], + createSshSigningKeyForAuthenticatedUser: ["POST /user/ssh_signing_keys"], + deleteEmailForAuthenticated: [ + "DELETE /user/emails", + {}, + { renamed: ["users", "deleteEmailForAuthenticatedUser"] } + ], + deleteEmailForAuthenticatedUser: ["DELETE /user/emails"], + deleteGpgKeyForAuthenticated: [ + "DELETE /user/gpg_keys/{gpg_key_id}", + {}, + { renamed: ["users", "deleteGpgKeyForAuthenticatedUser"] } + ], + deleteGpgKeyForAuthenticatedUser: ["DELETE /user/gpg_keys/{gpg_key_id}"], + deletePublicSshKeyForAuthenticated: [ + "DELETE /user/keys/{key_id}", + {}, + { renamed: ["users", "deletePublicSshKeyForAuthenticatedUser"] } + ], + deletePublicSshKeyForAuthenticatedUser: ["DELETE /user/keys/{key_id}"], + deleteSocialAccountForAuthenticatedUser: ["DELETE /user/social_accounts"], + deleteSshSigningKeyForAuthenticatedUser: [ + "DELETE /user/ssh_signing_keys/{ssh_signing_key_id}" + ], + follow: ["PUT /user/following/{username}"], + getAuthenticated: ["GET /user"], + getByUsername: ["GET /users/{username}"], + getContextForUser: ["GET /users/{username}/hovercard"], + getGpgKeyForAuthenticated: [ + "GET /user/gpg_keys/{gpg_key_id}", + {}, + { renamed: ["users", "getGpgKeyForAuthenticatedUser"] } + ], + getGpgKeyForAuthenticatedUser: ["GET /user/gpg_keys/{gpg_key_id}"], + getPublicSshKeyForAuthenticated: [ + "GET /user/keys/{key_id}", + {}, + { renamed: ["users", "getPublicSshKeyForAuthenticatedUser"] } + ], + getPublicSshKeyForAuthenticatedUser: ["GET /user/keys/{key_id}"], + getSshSigningKeyForAuthenticatedUser: [ + "GET /user/ssh_signing_keys/{ssh_signing_key_id}" + ], + list: ["GET /users"], + listBlockedByAuthenticated: [ + "GET /user/blocks", + {}, + { renamed: ["users", "listBlockedByAuthenticatedUser"] } + ], + listBlockedByAuthenticatedUser: ["GET /user/blocks"], + listEmailsForAuthenticated: [ + "GET /user/emails", + {}, + { renamed: ["users", "listEmailsForAuthenticatedUser"] } + ], + listEmailsForAuthenticatedUser: ["GET /user/emails"], + listFollowedByAuthenticated: [ + "GET /user/following", + {}, + { renamed: ["users", "listFollowedByAuthenticatedUser"] } + ], + listFollowedByAuthenticatedUser: ["GET /user/following"], + listFollowersForAuthenticatedUser: ["GET /user/followers"], + listFollowersForUser: ["GET /users/{username}/followers"], + listFollowingForUser: ["GET /users/{username}/following"], + listGpgKeysForAuthenticated: [ + "GET /user/gpg_keys", + {}, + { renamed: ["users", "listGpgKeysForAuthenticatedUser"] } + ], + listGpgKeysForAuthenticatedUser: ["GET /user/gpg_keys"], + listGpgKeysForUser: ["GET /users/{username}/gpg_keys"], + listPublicEmailsForAuthenticated: [ + "GET /user/public_emails", + {}, + { renamed: ["users", "listPublicEmailsForAuthenticatedUser"] } + ], + listPublicEmailsForAuthenticatedUser: ["GET /user/public_emails"], + listPublicKeysForUser: ["GET /users/{username}/keys"], + listPublicSshKeysForAuthenticated: [ + "GET /user/keys", + {}, + { renamed: ["users", "listPublicSshKeysForAuthenticatedUser"] } + ], + listPublicSshKeysForAuthenticatedUser: ["GET /user/keys"], + listSocialAccountsForAuthenticatedUser: ["GET /user/social_accounts"], + listSocialAccountsForUser: ["GET /users/{username}/social_accounts"], + listSshSigningKeysForAuthenticatedUser: ["GET /user/ssh_signing_keys"], + listSshSigningKeysForUser: ["GET /users/{username}/ssh_signing_keys"], + setPrimaryEmailVisibilityForAuthenticated: [ + "PATCH /user/email/visibility", + {}, + { renamed: ["users", "setPrimaryEmailVisibilityForAuthenticatedUser"] } + ], + setPrimaryEmailVisibilityForAuthenticatedUser: [ + "PATCH /user/email/visibility" + ], + unblock: ["DELETE /user/blocks/{username}"], + unfollow: ["DELETE /user/following/{username}"], + updateAuthenticated: ["PATCH /user"] + } +}; +var endpoints_default = Endpoints; + +// pkg/dist-src/endpoints-to-methods.js +var endpointMethodsMap = /* @__PURE__ */ new Map(); +for (const [scope, endpoints] of Object.entries(endpoints_default)) { + for (const [methodName, endpoint] of Object.entries(endpoints)) { + const [route, defaults, decorations] = endpoint; + const [method, url] = route.split(/ /); + const endpointDefaults = Object.assign( + { + method, + url + }, + defaults + ); + if (!endpointMethodsMap.has(scope)) { + endpointMethodsMap.set(scope, /* @__PURE__ */ new Map()); + } + endpointMethodsMap.get(scope).set(methodName, { + scope, + methodName, + endpointDefaults, + decorations + }); + } +} +var handler = { + has({ scope }, methodName) { + return endpointMethodsMap.get(scope).has(methodName); + }, + getOwnPropertyDescriptor(target, methodName) { + return { + value: this.get(target, methodName), + // ensures method is in the cache + configurable: true, + writable: true, + enumerable: true + }; + }, + defineProperty(target, methodName, descriptor) { + Object.defineProperty(target.cache, methodName, descriptor); + return true; + }, + deleteProperty(target, methodName) { + delete target.cache[methodName]; + return true; + }, + ownKeys({ scope }) { + return [...endpointMethodsMap.get(scope).keys()]; + }, + set(target, methodName, value) { + return target.cache[methodName] = value; + }, + get({ octokit, scope, cache }, methodName) { + if (cache[methodName]) { + return cache[methodName]; + } + const method = endpointMethodsMap.get(scope).get(methodName); + if (!method) { + return void 0; + } + const { endpointDefaults, decorations } = method; + if (decorations) { + cache[methodName] = decorate( + octokit, + scope, + methodName, + endpointDefaults, + decorations + ); + } else { + cache[methodName] = octokit.request.defaults(endpointDefaults); + } + return cache[methodName]; + } +}; +function endpointsToMethods(octokit) { + const newMethods = {}; + for (const scope of endpointMethodsMap.keys()) { + newMethods[scope] = new Proxy({ octokit, scope, cache: {} }, handler); + } + return newMethods; +} +function decorate(octokit, scope, methodName, defaults, decorations) { + const requestWithDefaults = octokit.request.defaults(defaults); + function withDecorations(...args) { + let options = requestWithDefaults.endpoint.merge(...args); + if (decorations.mapToData) { + options = Object.assign({}, options, { + data: options[decorations.mapToData], + [decorations.mapToData]: void 0 + }); + return requestWithDefaults(options); + } + if (decorations.renamed) { + const [newScope, newMethodName] = decorations.renamed; + octokit.log.warn( + `octokit.${scope}.${methodName}() has been renamed to octokit.${newScope}.${newMethodName}()` + ); + } + if (decorations.deprecated) { + octokit.log.warn(decorations.deprecated); + } + if (decorations.renamedParameters) { + const options2 = requestWithDefaults.endpoint.merge(...args); + for (const [name, alias] of Object.entries( + decorations.renamedParameters + )) { + if (name in options2) { + octokit.log.warn( + `"${name}" parameter is deprecated for "octokit.${scope}.${methodName}()". Use "${alias}" instead` + ); + if (!(alias in options2)) { + options2[alias] = options2[name]; + } + delete options2[name]; + } + } + return requestWithDefaults(options2); + } + return requestWithDefaults(...args); + } + return Object.assign(withDecorations, requestWithDefaults); +} + +// pkg/dist-src/index.js +function restEndpointMethods(octokit) { + const api = endpointsToMethods(octokit); + return { + rest: api + }; +} +restEndpointMethods.VERSION = VERSION; +function legacyRestEndpointMethods(octokit) { + const api = endpointsToMethods(octokit); + return { + ...api, + rest: api + }; +} +legacyRestEndpointMethods.VERSION = VERSION; +// Annotate the CommonJS export names for ESM import in node: +0 && (0); + + +/***/ }), + +/***/ 537: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// pkg/dist-src/index.js +var dist_src_exports = {}; +__export(dist_src_exports, { + RequestError: () => RequestError +}); +module.exports = __toCommonJS(dist_src_exports); +var import_deprecation = __nccwpck_require__(8932); +var import_once = __toESM(__nccwpck_require__(1223)); +var logOnceCode = (0, import_once.default)((deprecation) => console.warn(deprecation)); +var logOnceHeaders = (0, import_once.default)((deprecation) => console.warn(deprecation)); +var RequestError = class extends Error { + constructor(message, statusCode, options) { + super(message); + if (Error.captureStackTrace) { + Error.captureStackTrace(this, this.constructor); + } + this.name = "HttpError"; + this.status = statusCode; + let headers; + if ("headers" in options && typeof options.headers !== "undefined") { + headers = options.headers; + } + if ("response" in options) { + this.response = options.response; + headers = options.response.headers; + } + const requestCopy = Object.assign({}, options.request); + if (options.request.headers.authorization) { + requestCopy.headers = Object.assign({}, options.request.headers, { + authorization: options.request.headers.authorization.replace( + / .*$/, + " [REDACTED]" + ) + }); + } + requestCopy.url = requestCopy.url.replace(/\bclient_secret=\w+/g, "client_secret=[REDACTED]").replace(/\baccess_token=\w+/g, "access_token=[REDACTED]"); + this.request = requestCopy; + Object.defineProperty(this, "code", { + get() { + logOnceCode( + new import_deprecation.Deprecation( + "[@octokit/request-error] `error.code` is deprecated, use `error.status`." + ) + ); + return statusCode; + } + }); + Object.defineProperty(this, "headers", { + get() { + logOnceHeaders( + new import_deprecation.Deprecation( + "[@octokit/request-error] `error.headers` is deprecated, use `error.response.headers`." + ) + ); + return headers || {}; + } + }); + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (0); + + +/***/ }), + +/***/ 6234: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// pkg/dist-src/index.js +var dist_src_exports = {}; +__export(dist_src_exports, { + request: () => request +}); +module.exports = __toCommonJS(dist_src_exports); +var import_endpoint = __nccwpck_require__(9440); +var import_universal_user_agent = __nccwpck_require__(5030); + +// pkg/dist-src/version.js +var VERSION = "8.1.4"; + +// pkg/dist-src/fetch-wrapper.js +var import_is_plain_object = __nccwpck_require__(3287); +var import_request_error = __nccwpck_require__(537); + +// pkg/dist-src/get-buffer-response.js +function getBufferResponse(response) { + return response.arrayBuffer(); +} + +// pkg/dist-src/fetch-wrapper.js +function fetchWrapper(requestOptions) { + var _a, _b, _c; + const log = requestOptions.request && requestOptions.request.log ? requestOptions.request.log : console; + const parseSuccessResponseBody = ((_a = requestOptions.request) == null ? void 0 : _a.parseSuccessResponseBody) !== false; + if ((0, import_is_plain_object.isPlainObject)(requestOptions.body) || Array.isArray(requestOptions.body)) { + requestOptions.body = JSON.stringify(requestOptions.body); + } + let headers = {}; + let status; + let url; + let { fetch } = globalThis; + if ((_b = requestOptions.request) == null ? void 0 : _b.fetch) { + fetch = requestOptions.request.fetch; + } + if (!fetch) { + throw new Error( + "fetch is not set. Please pass a fetch implementation as new Octokit({ request: { fetch }}). Learn more at https://github.com/octokit/octokit.js/#fetch-missing" + ); + } + return fetch(requestOptions.url, { + method: requestOptions.method, + body: requestOptions.body, + headers: requestOptions.headers, + signal: (_c = requestOptions.request) == null ? void 0 : _c.signal, + // duplex must be set if request.body is ReadableStream or Async Iterables. + // See https://fetch.spec.whatwg.org/#dom-requestinit-duplex. + ...requestOptions.body && { duplex: "half" } + }).then(async (response) => { + url = response.url; + status = response.status; + for (const keyAndValue of response.headers) { + headers[keyAndValue[0]] = keyAndValue[1]; + } + if ("deprecation" in headers) { + const matches = headers.link && headers.link.match(/<([^>]+)>; rel="deprecation"/); + const deprecationLink = matches && matches.pop(); + log.warn( + `[@octokit/request] "${requestOptions.method} ${requestOptions.url}" is deprecated. It is scheduled to be removed on ${headers.sunset}${deprecationLink ? `. See ${deprecationLink}` : ""}` + ); + } + if (status === 204 || status === 205) { + return; + } + if (requestOptions.method === "HEAD") { + if (status < 400) { + return; + } + throw new import_request_error.RequestError(response.statusText, status, { + response: { + url, + status, + headers, + data: void 0 + }, + request: requestOptions + }); + } + if (status === 304) { + throw new import_request_error.RequestError("Not modified", status, { + response: { + url, + status, + headers, + data: await getResponseData(response) + }, + request: requestOptions + }); + } + if (status >= 400) { + const data = await getResponseData(response); + const error = new import_request_error.RequestError(toErrorMessage(data), status, { + response: { + url, + status, + headers, + data + }, + request: requestOptions + }); + throw error; + } + return parseSuccessResponseBody ? await getResponseData(response) : response.body; + }).then((data) => { + return { + status, + url, + headers, + data + }; + }).catch((error) => { + if (error instanceof import_request_error.RequestError) + throw error; + else if (error.name === "AbortError") + throw error; + let message = error.message; + if (error.name === "TypeError" && "cause" in error) { + if (error.cause instanceof Error) { + message = error.cause.message; + } else if (typeof error.cause === "string") { + message = error.cause; + } + } + throw new import_request_error.RequestError(message, 500, { + request: requestOptions + }); + }); +} +async function getResponseData(response) { + const contentType = response.headers.get("content-type"); + if (/application\/json/.test(contentType)) { + return response.json(); + } + if (!contentType || /^text\/|charset=utf-8$/.test(contentType)) { + return response.text(); + } + return getBufferResponse(response); +} +function toErrorMessage(data) { + if (typeof data === "string") + return data; + if ("message" in data) { + if (Array.isArray(data.errors)) { + return `${data.message}: ${data.errors.map(JSON.stringify).join(", ")}`; + } + return data.message; + } + return `Unknown error: ${JSON.stringify(data)}`; +} + +// pkg/dist-src/with-defaults.js +function withDefaults(oldEndpoint, newDefaults) { + const endpoint2 = oldEndpoint.defaults(newDefaults); + const newApi = function(route, parameters) { + const endpointOptions = endpoint2.merge(route, parameters); + if (!endpointOptions.request || !endpointOptions.request.hook) { + return fetchWrapper(endpoint2.parse(endpointOptions)); + } + const request2 = (route2, parameters2) => { + return fetchWrapper( + endpoint2.parse(endpoint2.merge(route2, parameters2)) + ); + }; + Object.assign(request2, { + endpoint: endpoint2, + defaults: withDefaults.bind(null, endpoint2) + }); + return endpointOptions.request.hook(request2, endpointOptions); + }; + return Object.assign(newApi, { + endpoint: endpoint2, + defaults: withDefaults.bind(null, endpoint2) + }); +} + +// pkg/dist-src/index.js +var request = withDefaults(import_endpoint.endpoint, { + headers: { + "user-agent": `octokit-request.js/${VERSION} ${(0, import_universal_user_agent.getUserAgent)()}` + } +}); +// Annotate the CommonJS export names for ESM import in node: +0 && (0); + + +/***/ }), + +/***/ 3682: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +var register = __nccwpck_require__(4670); +var addHook = __nccwpck_require__(5549); +var removeHook = __nccwpck_require__(6819); + +// bind with array of arguments: https://stackoverflow.com/a/21792913 +var bind = Function.bind; +var bindable = bind.bind(bind); + +function bindApi(hook, state, name) { + var removeHookRef = bindable(removeHook, null).apply( + null, + name ? [state, name] : [state] + ); + hook.api = { remove: removeHookRef }; + hook.remove = removeHookRef; + ["before", "error", "after", "wrap"].forEach(function (kind) { + var args = name ? [state, kind, name] : [state, kind]; + hook[kind] = hook.api[kind] = bindable(addHook, null).apply(null, args); + }); +} + +function HookSingular() { + var singularHookName = "h"; + var singularHookState = { + registry: {}, + }; + var singularHook = register.bind(null, singularHookState, singularHookName); + bindApi(singularHook, singularHookState, singularHookName); + return singularHook; +} + +function HookCollection() { + var state = { + registry: {}, + }; + + var hook = register.bind(null, state); + bindApi(hook, state); + + return hook; +} + +var collectionHookDeprecationMessageDisplayed = false; +function Hook() { + if (!collectionHookDeprecationMessageDisplayed) { + console.warn( + '[before-after-hook]: "Hook()" repurposing warning, use "Hook.Collection()". Read more: https://git.io/upgrade-before-after-hook-to-1.4' + ); + collectionHookDeprecationMessageDisplayed = true; + } + return HookCollection(); +} + +Hook.Singular = HookSingular.bind(); +Hook.Collection = HookCollection.bind(); + +module.exports = Hook; +// expose constructors as a named property for TypeScript +module.exports.Hook = Hook; +module.exports.Singular = Hook.Singular; +module.exports.Collection = Hook.Collection; + + +/***/ }), + +/***/ 5549: +/***/ ((module) => { + +module.exports = addHook; + +function addHook(state, kind, name, hook) { + var orig = hook; + if (!state.registry[name]) { + state.registry[name] = []; + } + + if (kind === "before") { + hook = function (method, options) { + return Promise.resolve() + .then(orig.bind(null, options)) + .then(method.bind(null, options)); + }; + } + + if (kind === "after") { + hook = function (method, options) { + var result; + return Promise.resolve() + .then(method.bind(null, options)) + .then(function (result_) { + result = result_; + return orig(result, options); + }) + .then(function () { + return result; + }); + }; + } + + if (kind === "error") { + hook = function (method, options) { + return Promise.resolve() + .then(method.bind(null, options)) + .catch(function (error) { + return orig(error, options); + }); + }; + } + + state.registry[name].push({ + hook: hook, + orig: orig, + }); +} + + +/***/ }), + +/***/ 4670: +/***/ ((module) => { + +module.exports = register; + +function register(state, name, method, options) { + if (typeof method !== "function") { + throw new Error("method for before hook must be a function"); + } + + if (!options) { + options = {}; + } + + if (Array.isArray(name)) { + return name.reverse().reduce(function (callback, name) { + return register.bind(null, state, name, callback, options); + }, method)(); + } + + return Promise.resolve().then(function () { + if (!state.registry[name]) { + return method(options); + } + + return state.registry[name].reduce(function (method, registered) { + return registered.hook.bind(null, method, options); + }, method)(); + }); +} + + +/***/ }), + +/***/ 6819: +/***/ ((module) => { + +module.exports = removeHook; + +function removeHook(state, name, method) { + if (!state.registry[name]) { + return; + } + + var index = state.registry[name] + .map(function (registered) { + return registered.orig; + }) + .indexOf(method); + + if (index === -1) { + return; + } + + state.registry[name].splice(index, 1); +} + + +/***/ }), + +/***/ 8932: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ value: true })); + +class Deprecation extends Error { + constructor(message) { + super(message); // Maintains proper stack trace (only available on V8) + + /* istanbul ignore next */ + + if (Error.captureStackTrace) { + Error.captureStackTrace(this, this.constructor); + } + + this.name = 'Deprecation'; + } + +} + +exports.Deprecation = Deprecation; + + +/***/ }), + +/***/ 3287: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ value: true })); + +/*! + * is-plain-object + * + * Copyright (c) 2014-2017, Jon Schlinkert. + * Released under the MIT License. + */ + +function isObject(o) { + return Object.prototype.toString.call(o) === '[object Object]'; +} + +function isPlainObject(o) { + var ctor,prot; + + if (isObject(o) === false) return false; + + // If has modified constructor + ctor = o.constructor; + if (ctor === undefined) return true; + + // If has modified prototype + prot = ctor.prototype; + if (isObject(prot) === false) return false; + + // If constructor does not have an Object-specific method + if (prot.hasOwnProperty('isPrototypeOf') === false) { + return false; + } + + // Most likely a plain Object + return true; +} + +exports.isPlainObject = isPlainObject; + + +/***/ }), + +/***/ 1223: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +var wrappy = __nccwpck_require__(2940) +module.exports = wrappy(once) +module.exports.strict = wrappy(onceStrict) + +once.proto = once(function () { + Object.defineProperty(Function.prototype, 'once', { + value: function () { + return once(this) + }, + configurable: true + }) + + Object.defineProperty(Function.prototype, 'onceStrict', { + value: function () { + return onceStrict(this) + }, + configurable: true + }) +}) + +function once (fn) { + var f = function () { + if (f.called) return f.value + f.called = true + return f.value = fn.apply(this, arguments) + } + f.called = false + return f +} + +function onceStrict (fn) { + var f = function () { + if (f.called) + throw new Error(f.onceError) + f.called = true + return f.value = fn.apply(this, arguments) + } + var name = fn.name || 'Function wrapped with `once`' + f.onceError = name + " shouldn't be called more than once" + f.called = false + return f +} + + +/***/ }), + +/***/ 877: +/***/ (function(module, __unused_webpack_exports, __nccwpck_require__) { + +/* @license +Papa Parse +v5.4.1 +https://github.com/mholt/PapaParse +License: MIT +*/ + +(function(root, factory) +{ + /* globals define */ + if (typeof define === 'function' && define.amd) + { + // AMD. Register as an anonymous module. + define([], factory); + } + else if (true) + { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(); + } + else + {} + // in strict mode we cannot access arguments.callee, so we need a named reference to + // stringify the factory method for the blob worker + // eslint-disable-next-line func-name +}(this, function moduleFactory() +{ + 'use strict'; + + var global = (function() { + // alternative method, similar to `Function('return this')()` + // but without using `eval` (which is disabled when + // using Content Security Policy). + + if (typeof self !== 'undefined') { return self; } + if (typeof window !== 'undefined') { return window; } + if (typeof global !== 'undefined') { return global; } + + // When running tests none of the above have been defined + return {}; + })(); + + + function getWorkerBlob() { + var URL = global.URL || global.webkitURL || null; + var code = moduleFactory.toString(); + return Papa.BLOB_URL || (Papa.BLOB_URL = URL.createObjectURL(new Blob(["var global = (function() { if (typeof self !== 'undefined') { return self; } if (typeof window !== 'undefined') { return window; } if (typeof global !== 'undefined') { return global; } return {}; })(); global.IS_PAPA_WORKER=true; ", '(', code, ')();'], {type: 'text/javascript'}))); + } + + var IS_WORKER = !global.document && !!global.postMessage, + IS_PAPA_WORKER = global.IS_PAPA_WORKER || false; + + var workers = {}, workerIdCounter = 0; + + var Papa = {}; + + Papa.parse = CsvToJson; + Papa.unparse = JsonToCsv; + + Papa.RECORD_SEP = String.fromCharCode(30); + Papa.UNIT_SEP = String.fromCharCode(31); + Papa.BYTE_ORDER_MARK = '\ufeff'; + Papa.BAD_DELIMITERS = ['\r', '\n', '"', Papa.BYTE_ORDER_MARK]; + Papa.WORKERS_SUPPORTED = !IS_WORKER && !!global.Worker; + Papa.NODE_STREAM_INPUT = 1; + + // Configurable chunk sizes for local and remote files, respectively + Papa.LocalChunkSize = 1024 * 1024 * 10; // 10 MB + Papa.RemoteChunkSize = 1024 * 1024 * 5; // 5 MB + Papa.DefaultDelimiter = ','; // Used if not specified and detection fails + + // Exposed for testing and development only + Papa.Parser = Parser; + Papa.ParserHandle = ParserHandle; + Papa.NetworkStreamer = NetworkStreamer; + Papa.FileStreamer = FileStreamer; + Papa.StringStreamer = StringStreamer; + Papa.ReadableStreamStreamer = ReadableStreamStreamer; + if (typeof PAPA_BROWSER_CONTEXT === 'undefined') { + Papa.DuplexStreamStreamer = DuplexStreamStreamer; + } + + if (global.jQuery) + { + var $ = global.jQuery; + $.fn.parse = function(options) + { + var config = options.config || {}; + var queue = []; + + this.each(function(idx) + { + var supported = $(this).prop('tagName').toUpperCase() === 'INPUT' + && $(this).attr('type').toLowerCase() === 'file' + && global.FileReader; + + if (!supported || !this.files || this.files.length === 0) + return true; // continue to next input element + + for (var i = 0; i < this.files.length; i++) + { + queue.push({ + file: this.files[i], + inputElem: this, + instanceConfig: $.extend({}, config) + }); + } + }); + + parseNextFile(); // begin parsing + return this; // maintains chainability + + + function parseNextFile() + { + if (queue.length === 0) + { + if (isFunction(options.complete)) + options.complete(); + return; + } + + var f = queue[0]; + + if (isFunction(options.before)) + { + var returned = options.before(f.file, f.inputElem); + + if (typeof returned === 'object') + { + if (returned.action === 'abort') + { + error('AbortError', f.file, f.inputElem, returned.reason); + return; // Aborts all queued files immediately + } + else if (returned.action === 'skip') + { + fileComplete(); // parse the next file in the queue, if any + return; + } + else if (typeof returned.config === 'object') + f.instanceConfig = $.extend(f.instanceConfig, returned.config); + } + else if (returned === 'skip') + { + fileComplete(); // parse the next file in the queue, if any + return; + } + } + + // Wrap up the user's complete callback, if any, so that ours also gets executed + var userCompleteFunc = f.instanceConfig.complete; + f.instanceConfig.complete = function(results) + { + if (isFunction(userCompleteFunc)) + userCompleteFunc(results, f.file, f.inputElem); + fileComplete(); + }; + + Papa.parse(f.file, f.instanceConfig); + } + + function error(name, file, elem, reason) + { + if (isFunction(options.error)) + options.error({name: name}, file, elem, reason); + } + + function fileComplete() + { + queue.splice(0, 1); + parseNextFile(); + } + }; + } + + + if (IS_PAPA_WORKER) + { + global.onmessage = workerThreadReceivedMessage; + } + + + + + function CsvToJson(_input, _config) + { + _config = _config || {}; + var dynamicTyping = _config.dynamicTyping || false; + if (isFunction(dynamicTyping)) { + _config.dynamicTypingFunction = dynamicTyping; + // Will be filled on first row call + dynamicTyping = {}; + } + _config.dynamicTyping = dynamicTyping; + + _config.transform = isFunction(_config.transform) ? _config.transform : false; + + if (_config.worker && Papa.WORKERS_SUPPORTED) + { + var w = newWorker(); + + w.userStep = _config.step; + w.userChunk = _config.chunk; + w.userComplete = _config.complete; + w.userError = _config.error; + + _config.step = isFunction(_config.step); + _config.chunk = isFunction(_config.chunk); + _config.complete = isFunction(_config.complete); + _config.error = isFunction(_config.error); + delete _config.worker; // prevent infinite loop + + w.postMessage({ + input: _input, + config: _config, + workerId: w.id + }); + + return; + } + + var streamer = null; + if (_input === Papa.NODE_STREAM_INPUT && typeof PAPA_BROWSER_CONTEXT === 'undefined') + { + // create a node Duplex stream for use + // with .pipe + streamer = new DuplexStreamStreamer(_config); + return streamer.getStream(); + } + else if (typeof _input === 'string') + { + _input = stripBom(_input); + if (_config.download) + streamer = new NetworkStreamer(_config); + else + streamer = new StringStreamer(_config); + } + else if (_input.readable === true && isFunction(_input.read) && isFunction(_input.on)) + { + streamer = new ReadableStreamStreamer(_config); + } + else if ((global.File && _input instanceof File) || _input instanceof Object) // ...Safari. (see issue #106) + streamer = new FileStreamer(_config); + + return streamer.stream(_input); + + // Strip character from UTF-8 BOM encoded files that cause issue parsing the file + function stripBom(string) { + if (string.charCodeAt(0) === 0xfeff) { + return string.slice(1); + } + return string; + } + } + + + + + + + function JsonToCsv(_input, _config) + { + // Default configuration + + /** whether to surround every datum with quotes */ + var _quotes = false; + + /** whether to write headers */ + var _writeHeader = true; + + /** delimiting character(s) */ + var _delimiter = ','; + + /** newline character(s) */ + var _newline = '\r\n'; + + /** quote character */ + var _quoteChar = '"'; + + /** escaped quote character, either "" or " */ + var _escapedQuote = _quoteChar + _quoteChar; + + /** whether to skip empty lines */ + var _skipEmptyLines = false; + + /** the columns (keys) we expect when we unparse objects */ + var _columns = null; + + /** whether to prevent outputting cells that can be parsed as formulae by spreadsheet software (Excel and LibreOffice) */ + var _escapeFormulae = false; + + unpackConfig(); + + var quoteCharRegex = new RegExp(escapeRegExp(_quoteChar), 'g'); + + if (typeof _input === 'string') + _input = JSON.parse(_input); + + if (Array.isArray(_input)) + { + if (!_input.length || Array.isArray(_input[0])) + return serialize(null, _input, _skipEmptyLines); + else if (typeof _input[0] === 'object') + return serialize(_columns || Object.keys(_input[0]), _input, _skipEmptyLines); + } + else if (typeof _input === 'object') + { + if (typeof _input.data === 'string') + _input.data = JSON.parse(_input.data); + + if (Array.isArray(_input.data)) + { + if (!_input.fields) + _input.fields = _input.meta && _input.meta.fields || _columns; + + if (!_input.fields) + _input.fields = Array.isArray(_input.data[0]) + ? _input.fields + : typeof _input.data[0] === 'object' + ? Object.keys(_input.data[0]) + : []; + + if (!(Array.isArray(_input.data[0])) && typeof _input.data[0] !== 'object') + _input.data = [_input.data]; // handles input like [1,2,3] or ['asdf'] + } + + return serialize(_input.fields || [], _input.data || [], _skipEmptyLines); + } + + // Default (any valid paths should return before this) + throw new Error('Unable to serialize unrecognized input'); + + + function unpackConfig() + { + if (typeof _config !== 'object') + return; + + if (typeof _config.delimiter === 'string' + && !Papa.BAD_DELIMITERS.filter(function(value) { return _config.delimiter.indexOf(value) !== -1; }).length) + { + _delimiter = _config.delimiter; + } + + if (typeof _config.quotes === 'boolean' + || typeof _config.quotes === 'function' + || Array.isArray(_config.quotes)) + _quotes = _config.quotes; + + if (typeof _config.skipEmptyLines === 'boolean' + || typeof _config.skipEmptyLines === 'string') + _skipEmptyLines = _config.skipEmptyLines; + + if (typeof _config.newline === 'string') + _newline = _config.newline; + + if (typeof _config.quoteChar === 'string') + _quoteChar = _config.quoteChar; + + if (typeof _config.header === 'boolean') + _writeHeader = _config.header; + + if (Array.isArray(_config.columns)) { + + if (_config.columns.length === 0) throw new Error('Option columns is empty'); + + _columns = _config.columns; + } + + if (_config.escapeChar !== undefined) { + _escapedQuote = _config.escapeChar + _quoteChar; + } + + if (typeof _config.escapeFormulae === 'boolean' || _config.escapeFormulae instanceof RegExp) { + _escapeFormulae = _config.escapeFormulae instanceof RegExp ? _config.escapeFormulae : /^[=+\-@\t\r].*$/; + } + } + + /** The double for loop that iterates the data and writes out a CSV string including header row */ + function serialize(fields, data, skipEmptyLines) + { + var csv = ''; + + if (typeof fields === 'string') + fields = JSON.parse(fields); + if (typeof data === 'string') + data = JSON.parse(data); + + var hasHeader = Array.isArray(fields) && fields.length > 0; + var dataKeyedByField = !(Array.isArray(data[0])); + + // If there a header row, write it first + if (hasHeader && _writeHeader) + { + for (var i = 0; i < fields.length; i++) + { + if (i > 0) + csv += _delimiter; + csv += safe(fields[i], i); + } + if (data.length > 0) + csv += _newline; + } + + // Then write out the data + for (var row = 0; row < data.length; row++) + { + var maxCol = hasHeader ? fields.length : data[row].length; + + var emptyLine = false; + var nullLine = hasHeader ? Object.keys(data[row]).length === 0 : data[row].length === 0; + if (skipEmptyLines && !hasHeader) + { + emptyLine = skipEmptyLines === 'greedy' ? data[row].join('').trim() === '' : data[row].length === 1 && data[row][0].length === 0; + } + if (skipEmptyLines === 'greedy' && hasHeader) { + var line = []; + for (var c = 0; c < maxCol; c++) { + var cx = dataKeyedByField ? fields[c] : c; + line.push(data[row][cx]); + } + emptyLine = line.join('').trim() === ''; + } + if (!emptyLine) + { + for (var col = 0; col < maxCol; col++) + { + if (col > 0 && !nullLine) + csv += _delimiter; + var colIdx = hasHeader && dataKeyedByField ? fields[col] : col; + csv += safe(data[row][colIdx], col); + } + if (row < data.length - 1 && (!skipEmptyLines || (maxCol > 0 && !nullLine))) + { + csv += _newline; + } + } + } + return csv; + } + + /** Encloses a value around quotes if needed (makes a value safe for CSV insertion) */ + function safe(str, col) + { + if (typeof str === 'undefined' || str === null) + return ''; + + if (str.constructor === Date) + return JSON.stringify(str).slice(1, 25); + + var needsQuotes = false; + + if (_escapeFormulae && typeof str === "string" && _escapeFormulae.test(str)) { + str = "'" + str; + needsQuotes = true; + } + + var escapedQuoteStr = str.toString().replace(quoteCharRegex, _escapedQuote); + + needsQuotes = needsQuotes + || _quotes === true + || (typeof _quotes === 'function' && _quotes(str, col)) + || (Array.isArray(_quotes) && _quotes[col]) + || hasAny(escapedQuoteStr, Papa.BAD_DELIMITERS) + || escapedQuoteStr.indexOf(_delimiter) > -1 + || escapedQuoteStr.charAt(0) === ' ' + || escapedQuoteStr.charAt(escapedQuoteStr.length - 1) === ' '; + + return needsQuotes ? _quoteChar + escapedQuoteStr + _quoteChar : escapedQuoteStr; + } + + function hasAny(str, substrings) + { + for (var i = 0; i < substrings.length; i++) + if (str.indexOf(substrings[i]) > -1) + return true; + return false; + } + } + + /** ChunkStreamer is the base prototype for various streamer implementations. */ + function ChunkStreamer(config) + { + this._handle = null; + this._finished = false; + this._completed = false; + this._halted = false; + this._input = null; + this._baseIndex = 0; + this._partialLine = ''; + this._rowCount = 0; + this._start = 0; + this._nextChunk = null; + this.isFirstChunk = true; + this._completeResults = { + data: [], + errors: [], + meta: {} + }; + replaceConfig.call(this, config); + + this.parseChunk = function(chunk, isFakeChunk) + { + // First chunk pre-processing + if (this.isFirstChunk && isFunction(this._config.beforeFirstChunk)) + { + var modifiedChunk = this._config.beforeFirstChunk(chunk); + if (modifiedChunk !== undefined) + chunk = modifiedChunk; + } + this.isFirstChunk = false; + this._halted = false; + + // Rejoin the line we likely just split in two by chunking the file + var aggregate = this._partialLine + chunk; + this._partialLine = ''; + + var results = this._handle.parse(aggregate, this._baseIndex, !this._finished); + + if (this._handle.paused() || this._handle.aborted()) { + this._halted = true; + return; + } + + var lastIndex = results.meta.cursor; + + if (!this._finished) + { + this._partialLine = aggregate.substring(lastIndex - this._baseIndex); + this._baseIndex = lastIndex; + } + + if (results && results.data) + this._rowCount += results.data.length; + + var finishedIncludingPreview = this._finished || (this._config.preview && this._rowCount >= this._config.preview); + + if (IS_PAPA_WORKER) + { + global.postMessage({ + results: results, + workerId: Papa.WORKER_ID, + finished: finishedIncludingPreview + }); + } + else if (isFunction(this._config.chunk) && !isFakeChunk) + { + this._config.chunk(results, this._handle); + if (this._handle.paused() || this._handle.aborted()) { + this._halted = true; + return; + } + results = undefined; + this._completeResults = undefined; + } + + if (!this._config.step && !this._config.chunk) { + this._completeResults.data = this._completeResults.data.concat(results.data); + this._completeResults.errors = this._completeResults.errors.concat(results.errors); + this._completeResults.meta = results.meta; + } + + if (!this._completed && finishedIncludingPreview && isFunction(this._config.complete) && (!results || !results.meta.aborted)) { + this._config.complete(this._completeResults, this._input); + this._completed = true; + } + + if (!finishedIncludingPreview && (!results || !results.meta.paused)) + this._nextChunk(); + + return results; + }; + + this._sendError = function(error) + { + if (isFunction(this._config.error)) + this._config.error(error); + else if (IS_PAPA_WORKER && this._config.error) + { + global.postMessage({ + workerId: Papa.WORKER_ID, + error: error, + finished: false + }); + } + }; + + function replaceConfig(config) + { + // Deep-copy the config so we can edit it + var configCopy = copy(config); + configCopy.chunkSize = parseInt(configCopy.chunkSize); // parseInt VERY important so we don't concatenate strings! + if (!config.step && !config.chunk) + configCopy.chunkSize = null; // disable Range header if not streaming; bad values break IIS - see issue #196 + this._handle = new ParserHandle(configCopy); + this._handle.streamer = this; + this._config = configCopy; // persist the copy to the caller + } + } + + + function NetworkStreamer(config) + { + config = config || {}; + if (!config.chunkSize) + config.chunkSize = Papa.RemoteChunkSize; + ChunkStreamer.call(this, config); + + var xhr; + + if (IS_WORKER) + { + this._nextChunk = function() + { + this._readChunk(); + this._chunkLoaded(); + }; + } + else + { + this._nextChunk = function() + { + this._readChunk(); + }; + } + + this.stream = function(url) + { + this._input = url; + this._nextChunk(); // Starts streaming + }; + + this._readChunk = function() + { + if (this._finished) + { + this._chunkLoaded(); + return; + } + + xhr = new XMLHttpRequest(); + + if (this._config.withCredentials) + { + xhr.withCredentials = this._config.withCredentials; + } + + if (!IS_WORKER) + { + xhr.onload = bindFunction(this._chunkLoaded, this); + xhr.onerror = bindFunction(this._chunkError, this); + } + + xhr.open(this._config.downloadRequestBody ? 'POST' : 'GET', this._input, !IS_WORKER); + // Headers can only be set when once the request state is OPENED + if (this._config.downloadRequestHeaders) + { + var headers = this._config.downloadRequestHeaders; + + for (var headerName in headers) + { + xhr.setRequestHeader(headerName, headers[headerName]); + } + } + + if (this._config.chunkSize) + { + var end = this._start + this._config.chunkSize - 1; // minus one because byte range is inclusive + xhr.setRequestHeader('Range', 'bytes=' + this._start + '-' + end); + } + + try { + xhr.send(this._config.downloadRequestBody); + } + catch (err) { + this._chunkError(err.message); + } + + if (IS_WORKER && xhr.status === 0) + this._chunkError(); + }; + + this._chunkLoaded = function() + { + if (xhr.readyState !== 4) + return; + + if (xhr.status < 200 || xhr.status >= 400) + { + this._chunkError(); + return; + } + + // Use chunckSize as it may be a diference on reponse lentgh due to characters with more than 1 byte + this._start += this._config.chunkSize ? this._config.chunkSize : xhr.responseText.length; + this._finished = !this._config.chunkSize || this._start >= getFileSize(xhr); + this.parseChunk(xhr.responseText); + }; + + this._chunkError = function(errorMessage) + { + var errorText = xhr.statusText || errorMessage; + this._sendError(new Error(errorText)); + }; + + function getFileSize(xhr) + { + var contentRange = xhr.getResponseHeader('Content-Range'); + if (contentRange === null) { // no content range, then finish! + return -1; + } + return parseInt(contentRange.substring(contentRange.lastIndexOf('/') + 1)); + } + } + NetworkStreamer.prototype = Object.create(ChunkStreamer.prototype); + NetworkStreamer.prototype.constructor = NetworkStreamer; + + + function FileStreamer(config) + { + config = config || {}; + if (!config.chunkSize) + config.chunkSize = Papa.LocalChunkSize; + ChunkStreamer.call(this, config); + + var reader, slice; + + // FileReader is better than FileReaderSync (even in worker) - see http://stackoverflow.com/q/24708649/1048862 + // But Firefox is a pill, too - see issue #76: https://github.com/mholt/PapaParse/issues/76 + var usingAsyncReader = typeof FileReader !== 'undefined'; // Safari doesn't consider it a function - see issue #105 + + this.stream = function(file) + { + this._input = file; + slice = file.slice || file.webkitSlice || file.mozSlice; + + if (usingAsyncReader) + { + reader = new FileReader(); // Preferred method of reading files, even in workers + reader.onload = bindFunction(this._chunkLoaded, this); + reader.onerror = bindFunction(this._chunkError, this); + } + else + reader = new FileReaderSync(); // Hack for running in a web worker in Firefox + + this._nextChunk(); // Starts streaming + }; + + this._nextChunk = function() + { + if (!this._finished && (!this._config.preview || this._rowCount < this._config.preview)) + this._readChunk(); + }; + + this._readChunk = function() + { + var input = this._input; + if (this._config.chunkSize) + { + var end = Math.min(this._start + this._config.chunkSize, this._input.size); + input = slice.call(input, this._start, end); + } + var txt = reader.readAsText(input, this._config.encoding); + if (!usingAsyncReader) + this._chunkLoaded({ target: { result: txt } }); // mimic the async signature + }; + + this._chunkLoaded = function(event) + { + // Very important to increment start each time before handling results + this._start += this._config.chunkSize; + this._finished = !this._config.chunkSize || this._start >= this._input.size; + this.parseChunk(event.target.result); + }; + + this._chunkError = function() + { + this._sendError(reader.error); + }; + + } + FileStreamer.prototype = Object.create(ChunkStreamer.prototype); + FileStreamer.prototype.constructor = FileStreamer; + + + function StringStreamer(config) + { + config = config || {}; + ChunkStreamer.call(this, config); + + var remaining; + this.stream = function(s) + { + remaining = s; + return this._nextChunk(); + }; + this._nextChunk = function() + { + if (this._finished) return; + var size = this._config.chunkSize; + var chunk; + if(size) { + chunk = remaining.substring(0, size); + remaining = remaining.substring(size); + } else { + chunk = remaining; + remaining = ''; + } + this._finished = !remaining; + return this.parseChunk(chunk); + }; + } + StringStreamer.prototype = Object.create(StringStreamer.prototype); + StringStreamer.prototype.constructor = StringStreamer; + + + function ReadableStreamStreamer(config) + { + config = config || {}; + + ChunkStreamer.call(this, config); + + var queue = []; + var parseOnData = true; + var streamHasEnded = false; + + this.pause = function() + { + ChunkStreamer.prototype.pause.apply(this, arguments); + this._input.pause(); + }; + + this.resume = function() + { + ChunkStreamer.prototype.resume.apply(this, arguments); + this._input.resume(); + }; + + this.stream = function(stream) + { + this._input = stream; + + this._input.on('data', this._streamData); + this._input.on('end', this._streamEnd); + this._input.on('error', this._streamError); + }; + + this._checkIsFinished = function() + { + if (streamHasEnded && queue.length === 1) { + this._finished = true; + } + }; + + this._nextChunk = function() + { + this._checkIsFinished(); + if (queue.length) + { + this.parseChunk(queue.shift()); + } + else + { + parseOnData = true; + } + }; + + this._streamData = bindFunction(function(chunk) + { + try + { + queue.push(typeof chunk === 'string' ? chunk : chunk.toString(this._config.encoding)); + + if (parseOnData) + { + parseOnData = false; + this._checkIsFinished(); + this.parseChunk(queue.shift()); + } + } + catch (error) + { + this._streamError(error); + } + }, this); + + this._streamError = bindFunction(function(error) + { + this._streamCleanUp(); + this._sendError(error); + }, this); + + this._streamEnd = bindFunction(function() + { + this._streamCleanUp(); + streamHasEnded = true; + this._streamData(''); + }, this); + + this._streamCleanUp = bindFunction(function() + { + this._input.removeListener('data', this._streamData); + this._input.removeListener('end', this._streamEnd); + this._input.removeListener('error', this._streamError); + }, this); + } + ReadableStreamStreamer.prototype = Object.create(ChunkStreamer.prototype); + ReadableStreamStreamer.prototype.constructor = ReadableStreamStreamer; + + + function DuplexStreamStreamer(_config) { + var Duplex = (__nccwpck_require__(2781).Duplex); + var config = copy(_config); + var parseOnWrite = true; + var writeStreamHasFinished = false; + var parseCallbackQueue = []; + var stream = null; + + this._onCsvData = function(results) + { + var data = results.data; + if (!stream.push(data) && !this._handle.paused()) { + // the writeable consumer buffer has filled up + // so we need to pause until more items + // can be processed + this._handle.pause(); + } + }; + + this._onCsvComplete = function() + { + // node will finish the read stream when + // null is pushed + stream.push(null); + }; + + config.step = bindFunction(this._onCsvData, this); + config.complete = bindFunction(this._onCsvComplete, this); + ChunkStreamer.call(this, config); + + this._nextChunk = function() + { + if (writeStreamHasFinished && parseCallbackQueue.length === 1) { + this._finished = true; + } + if (parseCallbackQueue.length) { + parseCallbackQueue.shift()(); + } else { + parseOnWrite = true; + } + }; + + this._addToParseQueue = function(chunk, callback) + { + // add to queue so that we can indicate + // completion via callback + // node will automatically pause the incoming stream + // when too many items have been added without their + // callback being invoked + parseCallbackQueue.push(bindFunction(function() { + this.parseChunk(typeof chunk === 'string' ? chunk : chunk.toString(config.encoding)); + if (isFunction(callback)) { + return callback(); + } + }, this)); + if (parseOnWrite) { + parseOnWrite = false; + this._nextChunk(); + } + }; + + this._onRead = function() + { + if (this._handle.paused()) { + // the writeable consumer can handle more data + // so resume the chunk parsing + this._handle.resume(); + } + }; + + this._onWrite = function(chunk, encoding, callback) + { + this._addToParseQueue(chunk, callback); + }; + + this._onWriteComplete = function() + { + writeStreamHasFinished = true; + // have to write empty string + // so parser knows its done + this._addToParseQueue(''); + }; + + this.getStream = function() + { + return stream; + }; + stream = new Duplex({ + readableObjectMode: true, + decodeStrings: false, + read: bindFunction(this._onRead, this), + write: bindFunction(this._onWrite, this) + }); + stream.once('finish', bindFunction(this._onWriteComplete, this)); + } + if (typeof PAPA_BROWSER_CONTEXT === 'undefined') { + DuplexStreamStreamer.prototype = Object.create(ChunkStreamer.prototype); + DuplexStreamStreamer.prototype.constructor = DuplexStreamStreamer; + } + + + // Use one ParserHandle per entire CSV file or string + function ParserHandle(_config) + { + // One goal is to minimize the use of regular expressions... + var MAX_FLOAT = Math.pow(2, 53); + var MIN_FLOAT = -MAX_FLOAT; + var FLOAT = /^\s*-?(\d+\.?|\.\d+|\d+\.\d+)([eE][-+]?\d+)?\s*$/; + var ISO_DATE = /^((\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z)))$/; + var self = this; + var _stepCounter = 0; // Number of times step was called (number of rows parsed) + var _rowCounter = 0; // Number of rows that have been parsed so far + var _input; // The input being parsed + var _parser; // The core parser being used + var _paused = false; // Whether we are paused or not + var _aborted = false; // Whether the parser has aborted or not + var _delimiterError; // Temporary state between delimiter detection and processing results + var _fields = []; // Fields are from the header row of the input, if there is one + var _results = { // The last results returned from the parser + data: [], + errors: [], + meta: {} + }; + + if (isFunction(_config.step)) + { + var userStep = _config.step; + _config.step = function(results) + { + _results = results; + + if (needsHeaderRow()) + processResults(); + else // only call user's step function after header row + { + processResults(); + + // It's possbile that this line was empty and there's no row here after all + if (_results.data.length === 0) + return; + + _stepCounter += results.data.length; + if (_config.preview && _stepCounter > _config.preview) + _parser.abort(); + else { + _results.data = _results.data[0]; + userStep(_results, self); + } + } + }; + } + + /** + * Parses input. Most users won't need, and shouldn't mess with, the baseIndex + * and ignoreLastRow parameters. They are used by streamers (wrapper functions) + * when an input comes in multiple chunks, like from a file. + */ + this.parse = function(input, baseIndex, ignoreLastRow) + { + var quoteChar = _config.quoteChar || '"'; + if (!_config.newline) + _config.newline = guessLineEndings(input, quoteChar); + + _delimiterError = false; + if (!_config.delimiter) + { + var delimGuess = guessDelimiter(input, _config.newline, _config.skipEmptyLines, _config.comments, _config.delimitersToGuess); + if (delimGuess.successful) + _config.delimiter = delimGuess.bestDelimiter; + else + { + _delimiterError = true; // add error after parsing (otherwise it would be overwritten) + _config.delimiter = Papa.DefaultDelimiter; + } + _results.meta.delimiter = _config.delimiter; + } + else if(isFunction(_config.delimiter)) + { + _config.delimiter = _config.delimiter(input); + _results.meta.delimiter = _config.delimiter; + } + + var parserConfig = copy(_config); + if (_config.preview && _config.header) + parserConfig.preview++; // to compensate for header row + + _input = input; + _parser = new Parser(parserConfig); + _results = _parser.parse(_input, baseIndex, ignoreLastRow); + processResults(); + return _paused ? { meta: { paused: true } } : (_results || { meta: { paused: false } }); + }; + + this.paused = function() + { + return _paused; + }; + + this.pause = function() + { + _paused = true; + _parser.abort(); + + // If it is streaming via "chunking", the reader will start appending correctly already so no need to substring, + // otherwise we can get duplicate content within a row + _input = isFunction(_config.chunk) ? "" : _input.substring(_parser.getCharIndex()); + }; + + this.resume = function() + { + if(self.streamer._halted) { + _paused = false; + self.streamer.parseChunk(_input, true); + } else { + // Bugfix: #636 In case the processing hasn't halted yet + // wait for it to halt in order to resume + setTimeout(self.resume, 3); + } + }; + + this.aborted = function() + { + return _aborted; + }; + + this.abort = function() + { + _aborted = true; + _parser.abort(); + _results.meta.aborted = true; + if (isFunction(_config.complete)) + _config.complete(_results); + _input = ''; + }; + + function testEmptyLine(s) { + return _config.skipEmptyLines === 'greedy' ? s.join('').trim() === '' : s.length === 1 && s[0].length === 0; + } + + function testFloat(s) { + if (FLOAT.test(s)) { + var floatValue = parseFloat(s); + if (floatValue > MIN_FLOAT && floatValue < MAX_FLOAT) { + return true; + } + } + return false; + } + + function processResults() + { + if (_results && _delimiterError) + { + addError('Delimiter', 'UndetectableDelimiter', 'Unable to auto-detect delimiting character; defaulted to \'' + Papa.DefaultDelimiter + '\''); + _delimiterError = false; + } + + if (_config.skipEmptyLines) + { + _results.data = _results.data.filter(function(d) { + return !testEmptyLine(d); + }); + } + + if (needsHeaderRow()) + fillHeaderFields(); + + return applyHeaderAndDynamicTypingAndTransformation(); + } + + function needsHeaderRow() + { + return _config.header && _fields.length === 0; + } + + function fillHeaderFields() + { + if (!_results) + return; + + function addHeader(header, i) + { + if (isFunction(_config.transformHeader)) + header = _config.transformHeader(header, i); + + _fields.push(header); + } + + if (Array.isArray(_results.data[0])) + { + for (var i = 0; needsHeaderRow() && i < _results.data.length; i++) + _results.data[i].forEach(addHeader); + + _results.data.splice(0, 1); + } + // if _results.data[0] is not an array, we are in a step where _results.data is the row. + else + _results.data.forEach(addHeader); + } + + function shouldApplyDynamicTyping(field) { + // Cache function values to avoid calling it for each row + if (_config.dynamicTypingFunction && _config.dynamicTyping[field] === undefined) { + _config.dynamicTyping[field] = _config.dynamicTypingFunction(field); + } + return (_config.dynamicTyping[field] || _config.dynamicTyping) === true; + } + + function parseDynamic(field, value) + { + if (shouldApplyDynamicTyping(field)) + { + if (value === 'true' || value === 'TRUE') + return true; + else if (value === 'false' || value === 'FALSE') + return false; + else if (testFloat(value)) + return parseFloat(value); + else if (ISO_DATE.test(value)) + return new Date(value); + else + return (value === '' ? null : value); + } + return value; + } + + function applyHeaderAndDynamicTypingAndTransformation() + { + if (!_results || (!_config.header && !_config.dynamicTyping && !_config.transform)) + return _results; + + function processRow(rowSource, i) + { + var row = _config.header ? {} : []; + + var j; + for (j = 0; j < rowSource.length; j++) + { + var field = j; + var value = rowSource[j]; + + if (_config.header) + field = j >= _fields.length ? '__parsed_extra' : _fields[j]; + + if (_config.transform) + value = _config.transform(value,field); + + value = parseDynamic(field, value); + + if (field === '__parsed_extra') + { + row[field] = row[field] || []; + row[field].push(value); + } + else + row[field] = value; + } + + + if (_config.header) + { + if (j > _fields.length) + addError('FieldMismatch', 'TooManyFields', 'Too many fields: expected ' + _fields.length + ' fields but parsed ' + j, _rowCounter + i); + else if (j < _fields.length) + addError('FieldMismatch', 'TooFewFields', 'Too few fields: expected ' + _fields.length + ' fields but parsed ' + j, _rowCounter + i); + } + + return row; + } + + var incrementBy = 1; + if (!_results.data.length || Array.isArray(_results.data[0])) + { + _results.data = _results.data.map(processRow); + incrementBy = _results.data.length; + } + else + _results.data = processRow(_results.data, 0); + + + if (_config.header && _results.meta) + _results.meta.fields = _fields; + + _rowCounter += incrementBy; + return _results; + } + + function guessDelimiter(input, newline, skipEmptyLines, comments, delimitersToGuess) { + var bestDelim, bestDelta, fieldCountPrevRow, maxFieldCount; + + delimitersToGuess = delimitersToGuess || [',', '\t', '|', ';', Papa.RECORD_SEP, Papa.UNIT_SEP]; + + for (var i = 0; i < delimitersToGuess.length; i++) { + var delim = delimitersToGuess[i]; + var delta = 0, avgFieldCount = 0, emptyLinesCount = 0; + fieldCountPrevRow = undefined; + + var preview = new Parser({ + comments: comments, + delimiter: delim, + newline: newline, + preview: 10 + }).parse(input); + + for (var j = 0; j < preview.data.length; j++) { + if (skipEmptyLines && testEmptyLine(preview.data[j])) { + emptyLinesCount++; + continue; + } + var fieldCount = preview.data[j].length; + avgFieldCount += fieldCount; + + if (typeof fieldCountPrevRow === 'undefined') { + fieldCountPrevRow = fieldCount; + continue; + } + else if (fieldCount > 0) { + delta += Math.abs(fieldCount - fieldCountPrevRow); + fieldCountPrevRow = fieldCount; + } + } + + if (preview.data.length > 0) + avgFieldCount /= (preview.data.length - emptyLinesCount); + + if ((typeof bestDelta === 'undefined' || delta <= bestDelta) + && (typeof maxFieldCount === 'undefined' || avgFieldCount > maxFieldCount) && avgFieldCount > 1.99) { + bestDelta = delta; + bestDelim = delim; + maxFieldCount = avgFieldCount; + } + } + + _config.delimiter = bestDelim; + + return { + successful: !!bestDelim, + bestDelimiter: bestDelim + }; + } + + function guessLineEndings(input, quoteChar) + { + input = input.substring(0, 1024 * 1024); // max length 1 MB + // Replace all the text inside quotes + var re = new RegExp(escapeRegExp(quoteChar) + '([^]*?)' + escapeRegExp(quoteChar), 'gm'); + input = input.replace(re, ''); + + var r = input.split('\r'); + + var n = input.split('\n'); + + var nAppearsFirst = (n.length > 1 && n[0].length < r[0].length); + + if (r.length === 1 || nAppearsFirst) + return '\n'; + + var numWithN = 0; + for (var i = 0; i < r.length; i++) + { + if (r[i][0] === '\n') + numWithN++; + } + + return numWithN >= r.length / 2 ? '\r\n' : '\r'; + } + + function addError(type, code, msg, row) + { + var error = { + type: type, + code: code, + message: msg + }; + if(row !== undefined) { + error.row = row; + } + _results.errors.push(error); + } + } + + /** https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions */ + function escapeRegExp(string) + { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string + } + + /** The core parser implements speedy and correct CSV parsing */ + function Parser(config) + { + // Unpack the config object + config = config || {}; + var delim = config.delimiter; + var newline = config.newline; + var comments = config.comments; + var step = config.step; + var preview = config.preview; + var fastMode = config.fastMode; + var quoteChar; + if (config.quoteChar === undefined || config.quoteChar === null) { + quoteChar = '"'; + } else { + quoteChar = config.quoteChar; + } + var escapeChar = quoteChar; + if (config.escapeChar !== undefined) { + escapeChar = config.escapeChar; + } + + // Delimiter must be valid + if (typeof delim !== 'string' + || Papa.BAD_DELIMITERS.indexOf(delim) > -1) + delim = ','; + + // Comment character must be valid + if (comments === delim) + throw new Error('Comment character same as delimiter'); + else if (comments === true) + comments = '#'; + else if (typeof comments !== 'string' + || Papa.BAD_DELIMITERS.indexOf(comments) > -1) + comments = false; + + // Newline must be valid: \r, \n, or \r\n + if (newline !== '\n' && newline !== '\r' && newline !== '\r\n') + newline = '\n'; + + // We're gonna need these at the Parser scope + var cursor = 0; + var aborted = false; + + this.parse = function(input, baseIndex, ignoreLastRow) + { + // For some reason, in Chrome, this speeds things up (!?) + if (typeof input !== 'string') + throw new Error('Input must be a string'); + + // We don't need to compute some of these every time parse() is called, + // but having them in a more local scope seems to perform better + var inputLen = input.length, + delimLen = delim.length, + newlineLen = newline.length, + commentsLen = comments.length; + var stepIsFunction = isFunction(step); + + // Establish starting state + cursor = 0; + var data = [], errors = [], row = [], lastCursor = 0; + + if (!input) + return returnable(); + + // Rename headers if there are duplicates + if (config.header && !baseIndex) + { + var firstLine = input.split(newline)[0]; + var headers = firstLine.split(delim); + var separator = '_'; + var headerMap = []; + var headerCount = {}; + var duplicateHeaders = false; + + for (var j in headers) { + var header = headers[j]; + if (isFunction(config.transformHeader)) + header = config.transformHeader(header, j); + var headerName = header; + + var count = headerCount[header] || 0; + if (count > 0) { + duplicateHeaders = true; + headerName = header + separator + count; + } + headerCount[header] = count + 1; + // In case it already exists, we add more separtors + while (headerMap.includes(headerName)) { + headerName = headerName + separator + count; + } + headerMap.push(headerName); + } + if (duplicateHeaders) { + var editedInput = input.split(newline); + editedInput[0] = headerMap.join(delim); + input = editedInput.join(newline); + } + } + if (fastMode || (fastMode !== false && input.indexOf(quoteChar) === -1)) + { + var rows = input.split(newline); + for (var i = 0; i < rows.length; i++) + { + row = rows[i]; + cursor += row.length; + if (i !== rows.length - 1) + cursor += newline.length; + else if (ignoreLastRow) + return returnable(); + if (comments && row.substring(0, commentsLen) === comments) + continue; + if (stepIsFunction) + { + data = []; + pushRow(row.split(delim)); + doStep(); + if (aborted) + return returnable(); + } + else + pushRow(row.split(delim)); + if (preview && i >= preview) + { + data = data.slice(0, preview); + return returnable(true); + } + } + return returnable(); + } + + var nextDelim = input.indexOf(delim, cursor); + var nextNewline = input.indexOf(newline, cursor); + var quoteCharRegex = new RegExp(escapeRegExp(escapeChar) + escapeRegExp(quoteChar), 'g'); + var quoteSearch = input.indexOf(quoteChar, cursor); + + // Parser loop + for (;;) + { + // Field has opening quote + if (input[cursor] === quoteChar) + { + // Start our search for the closing quote where the cursor is + quoteSearch = cursor; + + // Skip the opening quote + cursor++; + + for (;;) + { + // Find closing quote + quoteSearch = input.indexOf(quoteChar, quoteSearch + 1); + + //No other quotes are found - no other delimiters + if (quoteSearch === -1) + { + if (!ignoreLastRow) { + // No closing quote... what a pity + errors.push({ + type: 'Quotes', + code: 'MissingQuotes', + message: 'Quoted field unterminated', + row: data.length, // row has yet to be inserted + index: cursor + }); + } + return finish(); + } + + // Closing quote at EOF + if (quoteSearch === inputLen - 1) + { + var value = input.substring(cursor, quoteSearch).replace(quoteCharRegex, quoteChar); + return finish(value); + } + + // If this quote is escaped, it's part of the data; skip it + // If the quote character is the escape character, then check if the next character is the escape character + if (quoteChar === escapeChar && input[quoteSearch + 1] === escapeChar) + { + quoteSearch++; + continue; + } + + // If the quote character is not the escape character, then check if the previous character was the escape character + if (quoteChar !== escapeChar && quoteSearch !== 0 && input[quoteSearch - 1] === escapeChar) + { + continue; + } + + if(nextDelim !== -1 && nextDelim < (quoteSearch + 1)) { + nextDelim = input.indexOf(delim, (quoteSearch + 1)); + } + if(nextNewline !== -1 && nextNewline < (quoteSearch + 1)) { + nextNewline = input.indexOf(newline, (quoteSearch + 1)); + } + // Check up to nextDelim or nextNewline, whichever is closest + var checkUpTo = nextNewline === -1 ? nextDelim : Math.min(nextDelim, nextNewline); + var spacesBetweenQuoteAndDelimiter = extraSpaces(checkUpTo); + + // Closing quote followed by delimiter or 'unnecessary spaces + delimiter' + if (input.substr(quoteSearch + 1 + spacesBetweenQuoteAndDelimiter, delimLen) === delim) + { + row.push(input.substring(cursor, quoteSearch).replace(quoteCharRegex, quoteChar)); + cursor = quoteSearch + 1 + spacesBetweenQuoteAndDelimiter + delimLen; + + // If char after following delimiter is not quoteChar, we find next quote char position + if (input[quoteSearch + 1 + spacesBetweenQuoteAndDelimiter + delimLen] !== quoteChar) + { + quoteSearch = input.indexOf(quoteChar, cursor); + } + nextDelim = input.indexOf(delim, cursor); + nextNewline = input.indexOf(newline, cursor); + break; + } + + var spacesBetweenQuoteAndNewLine = extraSpaces(nextNewline); + + // Closing quote followed by newline or 'unnecessary spaces + newLine' + if (input.substring(quoteSearch + 1 + spacesBetweenQuoteAndNewLine, quoteSearch + 1 + spacesBetweenQuoteAndNewLine + newlineLen) === newline) + { + row.push(input.substring(cursor, quoteSearch).replace(quoteCharRegex, quoteChar)); + saveRow(quoteSearch + 1 + spacesBetweenQuoteAndNewLine + newlineLen); + nextDelim = input.indexOf(delim, cursor); // because we may have skipped the nextDelim in the quoted field + quoteSearch = input.indexOf(quoteChar, cursor); // we search for first quote in next line + + if (stepIsFunction) + { + doStep(); + if (aborted) + return returnable(); + } + + if (preview && data.length >= preview) + return returnable(true); + + break; + } + + + // Checks for valid closing quotes are complete (escaped quotes or quote followed by EOF/delimiter/newline) -- assume these quotes are part of an invalid text string + errors.push({ + type: 'Quotes', + code: 'InvalidQuotes', + message: 'Trailing quote on quoted field is malformed', + row: data.length, // row has yet to be inserted + index: cursor + }); + + quoteSearch++; + continue; + + } + + continue; + } + + // Comment found at start of new line + if (comments && row.length === 0 && input.substring(cursor, cursor + commentsLen) === comments) + { + if (nextNewline === -1) // Comment ends at EOF + return returnable(); + cursor = nextNewline + newlineLen; + nextNewline = input.indexOf(newline, cursor); + nextDelim = input.indexOf(delim, cursor); + continue; + } + + // Next delimiter comes before next newline, so we've reached end of field + if (nextDelim !== -1 && (nextDelim < nextNewline || nextNewline === -1)) + { + row.push(input.substring(cursor, nextDelim)); + cursor = nextDelim + delimLen; + // we look for next delimiter char + nextDelim = input.indexOf(delim, cursor); + continue; + } + + // End of row + if (nextNewline !== -1) + { + row.push(input.substring(cursor, nextNewline)); + saveRow(nextNewline + newlineLen); + + if (stepIsFunction) + { + doStep(); + if (aborted) + return returnable(); + } + + if (preview && data.length >= preview) + return returnable(true); + + continue; + } + + break; + } + + + return finish(); + + + function pushRow(row) + { + data.push(row); + lastCursor = cursor; + } + + /** + * checks if there are extra spaces after closing quote and given index without any text + * if Yes, returns the number of spaces + */ + function extraSpaces(index) { + var spaceLength = 0; + if (index !== -1) { + var textBetweenClosingQuoteAndIndex = input.substring(quoteSearch + 1, index); + if (textBetweenClosingQuoteAndIndex && textBetweenClosingQuoteAndIndex.trim() === '') { + spaceLength = textBetweenClosingQuoteAndIndex.length; + } + } + return spaceLength; + } + + /** + * Appends the remaining input from cursor to the end into + * row, saves the row, calls step, and returns the results. + */ + function finish(value) + { + if (ignoreLastRow) + return returnable(); + if (typeof value === 'undefined') + value = input.substring(cursor); + row.push(value); + cursor = inputLen; // important in case parsing is paused + pushRow(row); + if (stepIsFunction) + doStep(); + return returnable(); + } + + /** + * Appends the current row to the results. It sets the cursor + * to newCursor and finds the nextNewline. The caller should + * take care to execute user's step function and check for + * preview and end parsing if necessary. + */ + function saveRow(newCursor) + { + cursor = newCursor; + pushRow(row); + row = []; + nextNewline = input.indexOf(newline, cursor); + } + + /** Returns an object with the results, errors, and meta. */ + function returnable(stopped) + { + return { + data: data, + errors: errors, + meta: { + delimiter: delim, + linebreak: newline, + aborted: aborted, + truncated: !!stopped, + cursor: lastCursor + (baseIndex || 0) + } + }; + } + + /** Executes the user's step function and resets data & errors. */ + function doStep() + { + step(returnable()); + data = []; + errors = []; + } + }; + + /** Sets the abort flag */ + this.abort = function() + { + aborted = true; + }; + + /** Gets the cursor position */ + this.getCharIndex = function() + { + return cursor; + }; + } + + + function newWorker() + { + if (!Papa.WORKERS_SUPPORTED) + return false; + + var workerUrl = getWorkerBlob(); + var w = new global.Worker(workerUrl); + w.onmessage = mainThreadReceivedMessage; + w.id = workerIdCounter++; + workers[w.id] = w; + return w; + } + + /** Callback when main thread receives a message */ + function mainThreadReceivedMessage(e) + { + var msg = e.data; + var worker = workers[msg.workerId]; + var aborted = false; + + if (msg.error) + worker.userError(msg.error, msg.file); + else if (msg.results && msg.results.data) + { + var abort = function() { + aborted = true; + completeWorker(msg.workerId, { data: [], errors: [], meta: { aborted: true } }); + }; + + var handle = { + abort: abort, + pause: notImplemented, + resume: notImplemented + }; + + if (isFunction(worker.userStep)) + { + for (var i = 0; i < msg.results.data.length; i++) + { + worker.userStep({ + data: msg.results.data[i], + errors: msg.results.errors, + meta: msg.results.meta + }, handle); + if (aborted) + break; + } + delete msg.results; // free memory ASAP + } + else if (isFunction(worker.userChunk)) + { + worker.userChunk(msg.results, handle, msg.file); + delete msg.results; + } + } + + if (msg.finished && !aborted) + completeWorker(msg.workerId, msg.results); + } + + function completeWorker(workerId, results) { + var worker = workers[workerId]; + if (isFunction(worker.userComplete)) + worker.userComplete(results); + worker.terminate(); + delete workers[workerId]; + } + + function notImplemented() { + throw new Error('Not implemented.'); + } + + /** Callback when worker thread receives a message */ + function workerThreadReceivedMessage(e) + { + var msg = e.data; + + if (typeof Papa.WORKER_ID === 'undefined' && msg) + Papa.WORKER_ID = msg.workerId; + + if (typeof msg.input === 'string') + { + global.postMessage({ + workerId: Papa.WORKER_ID, + results: Papa.parse(msg.input, msg.config), + finished: true + }); + } + else if ((global.File && msg.input instanceof File) || msg.input instanceof Object) // thank you, Safari (see issue #106) + { + var results = Papa.parse(msg.input, msg.config); + if (results) + global.postMessage({ + workerId: Papa.WORKER_ID, + results: results, + finished: true + }); + } + } + + /** Makes a deep copy of an array or object (mostly) */ + function copy(obj) + { + if (typeof obj !== 'object' || obj === null) + return obj; + var cpy = Array.isArray(obj) ? [] : {}; + for (var key in obj) + cpy[key] = copy(obj[key]); + return cpy; + } + + function bindFunction(f, self) + { + return function() { f.apply(self, arguments); }; + } + + function isFunction(func) + { + return typeof func === 'function'; + } + + return Papa; +})); + + +/***/ }), + +/***/ 2582: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +const {Writable, Readable} = __nccwpck_require__(2781); +const {EventEmitter} = __nccwpck_require__(2361); + +class ReReadable extends Writable { + + constructor(options) { + + options = Object.assign({ + length: 1048576, + highWaterMark: 32, + dropInterval: 1e3 + }, options); + + super(options); + + this._readableOptions = options; + + this._highWaterMark = options.highWaterMark; + this._bufArrLength = options.length; + + this._reading = 0; + this._bufArr = []; + this.hiBufCr = 0; + this.loBufCr = 0; + this._waiting = null; + this._ended = new Promise((res) => this.on("finish", res)); + + } + + _write(chunk, encoding, callback) { + this._bufArr.push([chunk, encoding]); + if (this._bufArr.length > this._bufArrLength) { + this._waiting = callback; + this.drop(); + } else { + callback(); + } + this.emit("wrote"); + } + + _writev(chunks, callback) { + this._bufArr.push(...chunks.map(({chunk, encoding}) => [chunk, encoding])); + if (this._bufArr.length > this._bufArrLength) { + this._waiting = callback; + this.drop(); + } else { + callback(); + } + this.emit("wrote"); + } + + updateBufPosition(bufCr) { + this.hiBufCr = bufCr > this.hiBufCr ? bufCr : this.hiBufCr; + this.loBufCr = bufCr > this.loBufCr ? bufCr : this.loBufCr; + if (this._waiting && this.hiBufCr >= this._bufArrLength - this._highWaterMark) { + const cb = this._waiting; + this._waiting = null; + cb(); + } + } + + drop() { + if (this._bufArr.length > this._bufArrLength) + this.emit("drop", this._bufArr.splice(0, this._bufArr.length - this._bufArrLength).length); + } + + rewind() { + return this.tail(-1); + } + + tail(count) { + let end = false; + this._ended.then(() => ret._read(end = true)); + this.setMaxListeners(++this._reading + EventEmitter.defaultMaxListeners); + + const ret = new Readable(Object.assign(this._readableOptions, { + read: () => { + if (ret.bufCr < this._bufArr.length) { + while(ret.bufCr < this._bufArr.length) { // while there's anything to read + const resp = ret.push(...this._bufArr[ret.bufCr++]); // push to readable + if (!resp && !end) break; // until there's not willing to read and we're not ended + } + this.updateBufPosition(ret.bufCr); + } else if (!end) { + this.once("wrote", ret._read); + } + + if (end) + ret.push(null); + } + })); + + ret.bufCr = count < this._bufArr.length && count > 0 ? this._bufArr.length - count : 0; + + this.on("drop", (count) => { + ret.bufCr -= count; + if (ret.bufCr < 0) { + ret.emit("drop", -ret.bufCr); + ret.bufCr = 0; + } + }); + + return ret; + } +} + +module.exports = {ReReadable}; + + +/***/ }), + +/***/ 2172: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +const scramjet = __nccwpck_require__(4238); + +/** + * A facilitation stream created for easy splitting or parsing buffers. + * + * Useful for working on built-in Node.js streams from files, parsing binary formats etc. + * + * A simple use case would be: + * + * ```javascript + * fs.createReadStream('pixels.rgba') + * .pipe(new BufferStream) // pipe a buffer stream into scramjet + * .breakup(4) // split into 4 byte fragments + * .parse(buffer => [ + * buffer.readInt8(0), // the output is a stream of R,G,B and Alpha + * buffer.readInt8(1), // values from 0-255 in an array. + * buffer.readInt8(2), + * buffer.readInt8(3) + * ]); + * ``` + * + * @memberof module:scramjet. + * @borrows BufferStream#stringify as BufferStream#toStringStream + * @borrows BufferStream#shift as BufferStream#pop + * @borrows BufferStream#parse as BufferStream#toDataStream + * @extends DataStream + */ +class BufferStream extends scramjet.DataStream { + + /** + * Creates the BufferStream + * + * @param {DataStreamOptions} [opts={}] Stream options passed to superclass + * @test test/methods/buffer-stream-constructor.js + */ + constructor(...args) { + super(...args); + this.buffer = []; + } + + /** + * Shift Function + * + * @callback ShiftBufferCallback + * @memberof module:scramjet~ + * @param {Buffer|any} shifted shifted bytes + */ + + /** + * Shift given number of bytes from the original stream + * + * Works the same way as {@see DataStream.shift}, but in this case extracts + * the given number of bytes. + * + * @chainable + * @param {number} chars The number of bytes to shift + * @param {ShiftBufferCallback} func Function that receives a string of shifted bytes + * + * @test test/methods/string-stream-shift.js + */ + shift(bytes, func) { + const ret = Buffer.alloc(bytes); + const str = this.tap()._selfInstance(); + let offs = 0; + + const chunkHandler = (chunk) => { + const length = Math.min(ret.length - offs, chunk.length); + chunk.copy(ret, offs, 0, length); + offs += length; + if (length >= bytes) { + unHook() + .then( + () => { + str.write(chunk.slice(length)); + this.pipe(str); + } + ); + } + }; + + const endHandler = (...args) => { + if (ret.length < bytes) { + unHook(); + } + str.end(...args); + }; + + const errorHandler = str.emit.bind(str, "error"); + + const unHook = () => { + this.removeListener("data", chunkHandler); + this.removeListener("end", endHandler); + this.removeListener("error", errorHandler); + return func(ret); + }; + + + this.on("data", chunkHandler); + this.on("end", endHandler); + this.on("error", errorHandler); + + return str; + } + + /** + * Splits the buffer stream into buffer objects + * + * @chainable + * @param {string|Buffer} splitter the buffer or string that the stream + * should be split by. + * @return {BufferStream} the re-split buffer stream. + * @test test/methods/buffer-stream-split.js + */ + split(splitter) { + if (splitter instanceof Buffer || typeof splitter === "string") { + const needle = Buffer.from(splitter); + return this.tap().pipe(this._selfInstance({ + transform(buffer, enc, callback) { + if (Buffer.isBuffer(this._haystack) && this._haystack.length > 0) { + this._haystack = Buffer.from([this._haystack, buffer]); + } else { + this._haystack = buffer; + } + + let pos; + while((pos = this._haystack.indexOf(needle)) > -1) { + this.push(Buffer.from(this._haystack.slice(0, pos))); + this._haystack = this._haystack.slice(pos + needle.length); + } + + callback(); + }, + flush(callback) { + if (this._haystack && this._haystack.length) this.push(this._haystack); + + this._haystack = null; + callback(); + } + })); + } + } + + /** + * Breaks up a stream apart into chunks of the specified length + * + * @chainable + * @param {number} number the desired chunk length + * @return {BufferStream} the resulting buffer stream. + * @test test/methods/buffer-stream-breakup.js + */ + breakup(number) { + if (number <= 0 || !isFinite(+number)) + throw new Error("Breakup number is invalid - must be a positive, finite integer."); + + return this.tap().pipe(this._selfInstance({ + transform(chunk, encoding, callback) { + if (Buffer.isBuffer(this.buffer)) { + chunk = Buffer.concat([this.buffer, chunk]); + } + let offset; + for (offset = 0; offset < chunk.length - number; offset += number) { + this.push(chunk.slice(offset, offset + number)); + } + this.buffer = chunk.slice(offset); + callback(); + }, + flush(callback) { + this.push(this.buffer); + this.buffer = null; + callback(); + } + })); + + } + + /** + * Creates a string stream from the given buffer stream + * + * Still it returns a DataStream derivative and isn't the typical node.js + * stream so you can do all your transforms when you like. + * + * @param {string|any} [encoding="utf-8"] The encoding to be used to convert the buffers + * to streams. + * @return {StringStream} The converted stream. + * @test test/methods/buffer-stream-tostringstream.js + */ + stringify(encoding = "utf-8") { + return this.pipe(new scramjet.StringStream(encoding, {objectMode: true})); + } + + /** + * @callback BufferParseCallback + * @memberof module:scramjet~ + * @param {Buffer} chunk the transformed chunk + * @return {Promise|any} the promise should be resolved with the parsed object + */ + + /** + * Parses every buffer to object + * + * The method MUST parse EVERY buffer into a single object, so the buffer + * stream here should already be split or broken up. + * + * @param {BufferParseCallback} parser The transform function + * @return {DataStream} The parsed objects stream. + * @test test/methods/buffer-stream-parse.js + */ + parse(parser) { + return this.tap().map(parser, scramjet.DataStream); + } + + /** + * @meta.noReadme + * @ignore + */ + _transform(chunk, encoding, callback) { + this.push(Buffer.from(chunk, encoding)); + return callback(); + } + + /** + * Creates a pipeline of streams and returns a scramjet stream. + * + * @see DataStream.pipeline + * @static + * @param {Array|Iterable|AsyncGeneratorFunction|GeneratorFunction|AsyncFunction|Function|string|Readable} readable the initial readable argument that is streamable by scramjet.from + * @param {Array} ...transforms Transform functions (as in {@link DataStream..use}) or Transform streams (any number of these as consecutive arguments) + * + * @returns {BufferStream} a new StringStream instance of the resulting pipeline + */ + static pipeline(...args) { + return scramjet.DataStream.pipeline.call(this, ...args); + } + + /** + * Create BufferStream from anything. + * + * @see module:scramjet.from + * + * @param {Array|Iterable|AsyncGeneratorFunction|GeneratorFunction|AsyncFunction|Function|Readable} stream argument to be turned into new stream + * @param {DataStreamOptions|Writable} [options={}] options passed to the new stream if created + * @return {BufferStream} new StringStream. + */ + static from(...args) { + return scramjet.DataStream.from.call(this, ...args); + } + +} + +BufferStream.prototype.pop = BufferStream.prototype.shift; +BufferStream.prototype.toDataStream = BufferStream.prototype.parse; +BufferStream.prototype.toStringStream = BufferStream.prototype.stringify; + +module.exports = BufferStream; + + +/***/ }), + +/***/ 7248: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +const {PromiseTransformStream} = __nccwpck_require__(9421); +const {Readable, Writable, Transform} = __nccwpck_require__(2781); +const scramjet = __nccwpck_require__(4238); + +const { + AsyncGeneratorFunction, + GeneratorFunction, + resolveCalleeBlackboxed, + pipeIfTarget +} = __nccwpck_require__(8603); + +/** + * DataStream is the primary stream type for Scramjet. When you parse your + * stream, just pipe it you can then perform calculations on the data objects + * streamed through your flow. + * + * Use as: + * + * ```javascript + * const { DataStream } = require('scramjet'); + * + * await (DataStream.from(aStream) // create a DataStream + * .map(findInFiles) // read some data asynchronously + * .map(sendToAPI) // send the data somewhere + * .run()); // wait until end + * ``` + * @memberof module:scramjet. + * @alias DataStream + * @borrows module:scramjet.DataStream#bufferify as module:scramjet.DataStream#toBufferStream + * @borrows module:scramjet.DataStream#stringify as module:scramjet.DataStream#toStringStream + * @extends import("stream").PassThrough + */ +class DataStream extends PromiseTransformStream { + + /** + * Create the DataStream. + * + * @param {DataStreamOptions} [opts={}] Stream options passed to superclass + * + * @test test/methods/data-stream-constructor.js + */ + constructor(opts) { + super(Object.assign({ + objectMode: true, + writableObjectMode: true, + readableObjectMode: true + }, opts)); + } + + /** + * Returns a DataStream from pretty much anything sensibly possible. + * + * Depending on type: + * * `self` will return self immediately + * * `Readable` stream will get piped to the current stream with errors forwarded + * * `Array` will get iterated and all items will be pushed to the returned stream. + * The stream will also be ended in such case. + * * `GeneratorFunction` will get executed to return the iterator which will be used as source for items + * * `AsyncGeneratorFunction` will also work as above (including generators) in node v10. + * * `Iterable`s iterator will be used as a source for streams + * + * You can also pass a `Function` or `AsyncFunction` that will be executed and it's outcome will be + * passed again to `from` and piped to the initially returned stream. Any additional arguments will be + * passed as arguments to the function. + * + * If a `String` is passed, scramjet will attempt to resolve it as a module and use the outcome + * as an argument to `from` as in the Function case described above. For more information see {@link modules.md} + * + * A simple example from a generator: + * + * ```javascript + * DataStream + * .from(function* () { + * while(x < 100) yield {x: x++}; + * }) + * .each(console.log) + * // {x: 0} + * // {x: 1} + * // ... + * // {x: 99} + * ``` + * + * @param {Array|Iterable|AsyncGeneratorFunction|GeneratorFunction|AsyncFunction|Promise|Function|string|Readable} input argument to be turned into new stream + * @param {DataStreamOptions|Writable} [options={}] options for creation of a new stream or the target stream + * @param {any[]} ...args additional arguments for the stream - will be passed to the function or generator + * @return {DataStream} + */ + static from(input, options, ...args) { + const target = options instanceof this && options; + + const {errors: {StreamError}} = scramjet; + + if (input instanceof this) { + return target ? input.pipe(target) : input; + } + + if (input instanceof Readable || ( + typeof input.readable === "boolean" && + typeof input.pipe === "function" && + typeof input.on === "function" + )) { + const out = target || new this( + Object.assign( + {}, + options, + { referrer: input instanceof DataStream ? input : null } + ) + ); + + input.pipe(out); + input.on("error", e => out.raise(e)); + return out; + } + + if (input instanceof GeneratorFunction || input instanceof AsyncGeneratorFunction) { + const iterator = input(...args); + const iteratorStream = this.fromIterator(iterator, options); + return pipeIfTarget(iteratorStream, target); + } + + if (Array.isArray(input)) + return pipeIfTarget(this.fromArray(input, options), target); + + const iter = input[Symbol.iterator] || (Symbol.asyncIterator && input[Symbol.asyncIterator]); + if (iter) { + try { + const iterator = iter.call(input); + return pipeIfTarget(this.fromIterator(iterator, options), target); + } catch(e) { + const out = target || new this(); + out.raise(new StreamError(e, out, "EXTERNAL", null)); + + return out; + } + } + + if (input instanceof Promise) { + const out = new this(Object.assign({}, options)); + + input + .then(source => this.from(source, out)) + .catch(e => out.raise(new StreamError(e, out, "EXTERNAL", null))); + + return out; + } + + if (typeof input === "function") { + const out = new this(Object.assign({}, options)); + + Promise.resolve(options) + .then(input) + .then(source => this.from(source, out)) + .catch(e => out.raise(new StreamError(e, out, "EXTERNAL", null))); + + return out; + } + + if (typeof input === "string") { + return new DataStream([]).use(input, ...args); + } + + throw new Error("Cannot return a stream from passed object"); + } + + /** + * @callback MapCallback + * @memberof module:scramjet~ + * @param {any} chunk the chunk to be mapped + * @returns {Promise|any} the mapped object + */ + + /** + * Transforms stream objects into new ones, just like Array.prototype.map + * does. + * + * Map takes an argument which is the Function function operating on every element + * of the stream. If the function returns a Promise or is an AsyncFunction then the + * stream will await for the outcome of the operation before pushing the data forwards. + * + * A simple example that turns stream of urls into stream of responses + * + * ```javascript + * stream.map(async url => fetch(url)); + * ``` + * + * Multiple subsequent map operations (as well as filter, do, each and other simple ops) + * will be merged together into a single operation to improve performance. Such behaviour + * can be suppressed by chaining `.tap()` after `.map()`. + * + * @param {MapCallback} func The function that creates the new object + * @param {function(new:DataStream)} [ClassType=this.constructor] The class to be mapped to. + * @chainable + * + * @test test/methods/data-stream-map.js + */ + map(func, ClassType = this.constructor) { + return this.pipe(new ClassType({ + promiseTransform: func, + referrer: this + })); + } + + /** + * @callback FilterCallback + * @memberof module:scramjet~ + * @param {any} chunk the chunk to be filtered or not + * @returns {Promise|Boolean} information if the object should remain in the filtered stream. + */ + + /** + * Filters object based on the function outcome, just like Array.prototype.filter. + * + * Filter takes a Function argument which should be a Function or an AsyncFunction that + * will be called on each stream item. If the outcome of the operation is `falsy` (`0`, `''`, + * `false`, `null` or `undefined`) the item will be filtered from subsequent operations + * and will not be pushed to the output of the stream. Otherwise the item will not be affected. + * + * A simple example that filters out non-2xx responses from a stream + * + * ```javascript + * stream.filter(({statusCode}) => !(statusCode >= 200 && statusCode < 300)); + * ``` + * + * @chainable + * @param {FilterCallback} func The function that filters the object + * + * @test test/methods/data-stream-filter.js + */ + filter(func) { + return this.pipe(this._selfInstance({ + promiseTransform: func, + afterTransform: (chunk, ret) => ret ? chunk : Promise.reject(DataStream.filter), + referrer: this + })); + } + + /** + * @callback ReduceCallback + * @memberof module:scramjet~ + * @param {any} accumulator the accumulator - the object initially passed or returned by the previous reduce operation + * @param {object} chunk the stream chunk. + * @return {Promise|any} accumulator for the next pass + */ + + /** + * Reduces the stream into a given accumulator + * + * Works similarly to Array.prototype.reduce, so whatever you return in the + * former operation will be the first operand to the latter. The result is a + * promise that's resolved with the return value of the last transform executed. + * + * A simple example that sums values from a stream + * + * ```javascript + * stream.reduce((accumulator, {value}) => accumulator + value); + * ``` + * + * This method is serial - meaning that any processing on an entry will + * occur only after the previous entry is fully processed. This does mean + * it's much slower than parallel functions. + * + * @async + * @param {ReduceCallback} func The into object will be passed as the first argument, the data object from the stream as the second. + * @param {object} into Any object passed initially to the transform function + * + * @test test/methods/data-stream-reduce.js + */ + reduce(func, into) { + + let last = Promise.resolve(into); + + return this.tap() + .pipe(new PromiseTransformStream({ + promiseTransform: (chunk) => { + return last = last.then((acc) => func(acc, chunk)); + }, + referrer: this, + initial: into + })) + .resume() + .whenFinished() + .then(() => last); + } + + /** + * @callback DoCallback + * @memberof module:scramjet~ + * @async + * @param {object} chunk source stream chunk + * @returns {Promise|any} the outcome is discarded + */ + + /** + * Perform an asynchronous operation without changing or resuming the stream. + * + * In essence the stream will use the call to keep the backpressure, but the resolving value + * has no impact on the streamed data (except for possible mutation of the chunk itself) + * + * @chainable + * @param {DoCallback} func the async function + */ + do(func) { + return this.map(async (chunk) => (await func(chunk), chunk)); + } + + /** + * Processes a number of functions in parallel, returns a stream of arrays of results. + * + * This method is to allow running multiple asynchronous operations and receive all the + * results at one, just like Promise.all behaves. + * + * Keep in mind that if one of your methods rejects, this behaves just like Promise.all + * you won't be able to receive partial results. + * + * @chainable + * @param {Function[]} functions list of async functions to run + * + * @test test/methods/data-stream-all.js + */ + all(functions) { + return this.map(chunk => { + const chunkPromise = Promise.resolve(chunk); + return Promise.all(functions.map(func => chunkPromise.then(func))); + }); + } + + /** + * Processes a number of functions in parallel, returns the first resolved. + * + * This method is to allow running multiple asynchronous operations awaiting just the + * result of the quickest to execute, just like Promise.race behaves. + * + * Keep in mind that if one of your methods it will only raise an error if that was + * the first method to reject. + * + * @chainable + * @param {Function[]} functions list of async functions to run + * + * @test test/methods/data-stream-race.js + */ + race(functions) { + return this.map(chunk => { + const chunkPromise = Promise.resolve(chunk); + return Promise.race(functions.map(func => chunkPromise.then(func))); + }); + } + + /** + * Allows processing items without keeping order + * + * This method useful if you are not concerned about the order in which the + * chunks are being pushed out of the operation. The `maxParallel` option is + * still used for keeping a number of simultaneous number of parallel operations + * that are currently happening. + * + * @param {MapCallback} func the async function that will be unordered + */ + unorder(func) { + const waiting = []; + const processing = Array(this._options.maxParallel).fill(null); + const out = this._selfInstance({referrer: this}); + + this + .each(async chunk => { + // we're using this race condition on purpose + /* eslint-disable require-atomic-updates */ + let slot = processing.findIndex(x => x === null); + if (slot < 0 && processing.length < this._options.maxParallel) slot = processing.length; + if (slot < 0) slot = await new Promise(res => waiting.push(res)); + + processing[slot] = Promise + .resolve(chunk) + .then(func) + .then(result => out.whenWrote(result)) + .then(() => { + const next = waiting.shift(); + if (next) next(slot); + else processing[slot] = null; + }); + /* eslint-enable require-atomic-updates */ + }) + .run() + .then(() => Promise.all(processing)) + .then(() => out.end()); + + return out; + } + + /** + * @callback IntoCallback + * @memberof module:scramjet~ + * @async + * @param {*} into stream passed to the into method + * @param {any} chunk source stream chunk + * @return {Promise|any} resolution for the old stream (for flow control only) + */ + + /** + * Allows own implementation of stream chaining. + * + * The async Function is called on every chunk and should implement writes in it's own way. The + * resolution will be awaited for flow control. The passed `into` argument is passed as the first + * argument to every call. + * + * It returns the DataStream passed as the second argument. + * + * @chainable + * @param {IntoCallback} func the method that processes incoming chunks + * @param {DataStream} into the DataStream derived class + * + * @test test/methods/data-stream-into.js + */ + into(func, into) { + if (!(into instanceof DataStream)) throw new Error("Stream must be passed!"); + + if (!into._options.referrer) + into.setOptions({referrer: this}); + + this.tap() + .catch(e => into.raise(e)) + .pipe(new (this.constructor)({ + promiseTransform: async (chunk) => { + try { + await func(into, chunk); + } catch(e) { + into.raise(e); + } + }, + referrer: this + })) + .on("end", () => into.end()) + .resume(); + + return into; + } + + /** + * @callback UseCallback + * @memberof module:scramjet~ + * @async + * @param {DataStream} stream + * @param {any[]} ...parameters + * @returns {DataStream} + */ + + /** + * Calls the passed method in place with the stream as first argument, returns result. + * + * The main intention of this method is to run scramjet modules - transforms that allow complex transforms of + * streams. These modules can also be run with [scramjet-cli](https://github.com/signicode/scramjet-cli) directly + * from the command line. + * + * @chainable + * @param {AsyncGeneratorFunction|GeneratorFunction|UseCallback|string|Readable} func if passed, the function will be called on self to add an option to inspect the stream in place, while not breaking the transform chain. Alternatively this can be a relative path to a scramjet-module. Lastly it can be a Transform stream. + * @param {any[]} ...parameters any additional parameters top be passed to the module + * @test test/methods/data-stream-use.js + */ + use(func, ...parameters) { + if (typeof func == "string") { + func = require(func.startsWith(".") ? resolveCalleeBlackboxed(func) : func); + } + + if (func instanceof Transform || ( + typeof func.readable === "boolean" && func.readable && + typeof func.writable === "boolean" && func.writable && + typeof func.pipe === "function" && + typeof func.on === "function" + )) { + return this.constructor.from(this.pipe(func)); + } + + if (func instanceof GeneratorFunction || func instanceof AsyncGeneratorFunction) { + return this.constructor.from(func, {}, this, ...parameters); + } + + if (typeof func === "function") { + const result = func(this, ...parameters); + if (result instanceof Promise) { + const out = new this.constructor(); + result + .then(res => this.constructor.from(res).pipe(out)) + .catch(e => out.raise(e)); + + return out; + } else { + return result; + } + } + + throw new Error("Unknown argument type."); + } + + /** + * Consumes all stream items doing nothing. Resolves when the stream is ended. + * + * This is very convienient if you're looking to use up the stream in operations that work on each entry like `map`. This uncorks the stream + * and allows all preceding operations to be run at any speed. + * + * All the data of the current stream will be discarded. + * + * The function returns a promise that is resolved when the stream ends. + * + * @async + */ + async run() { + return this.tap() + .pipe(new DataStream()) + .on("data", () => 0) + .whenEnd(); + } + + /** + * Creates a pipeline of streams and returns a scramjet stream. + * + * This is similar to node.js stream pipeline method, but also takes scramjet modules + * as possibilities in an array of transforms. It may be used to run a series of non-scramjet + * transform streams. + * + * The first argument is anything streamable and will be sanitized by {@link DataStream..from}. + * + * Each following argument will be understood as a transform and can be any of: + * * AsyncFunction or Function - will be executed by {@link DataStream..use} + * * A transform stream that will be piped to the preceding stream + * + * @param {Array|Iterable|AsyncGeneratorFunction|GeneratorFunction|AsyncFunction|Function|string|Readable} readable the initial readable argument that is streamable by scramjet.from + * @param {Array} ...transforms Transform functions (as in {@link DataStream..use}) or Transform streams (any number of these as consecutive arguments) + * + * @returns {DataStream} a new DataStream instance of the resulting pipeline + */ + static pipeline(readable, ...transforms) { + const out = new this(); + let current = this.from(readable); + + (async () => { + for (let transform of transforms) { + if (transform instanceof Transform || ( + typeof transform.readable === "boolean" && transform.readable && + typeof transform.writable === "boolean" && transform.writable && + typeof transform.pipe === "function" && + typeof transform.on === "function" + )) { + current = this.from(current.pipe(transform)); + } else { + current = this.from(current).use(transform); + } + } + + this + .from(current) + .pipe(out); + + })() + .then() + .catch(e => { + return out.raise(e); + }); + + return out; + } + + /** + * Stops merging transform Functions at the current place in the command chain. + * + * @name tap + * @chainable + * @memberof module:scramjet.DataStream# + * @method + * @test test/methods/data-stream-tap.js + */ + + /** + * Reads a chunk from the stream and resolves the promise when read. + * + * @async + * @name whenRead + * @memberof module:scramjet.DataStream# + * @method + */ + + /** + * Writes a chunk to the stream and returns a Promise resolved when more chunks can be written. + * + * @async + * @name whenWrote + * @memberof module:scramjet.DataStream# + * @method + * @param {*} chunk a chunk to write + * @param {any[]} ...more more chunks to write + */ + + /** + * Resolves when stream ends - rejects on uncaught error + * + * @async + * @name whenEnd + * @memberof module:scramjet.DataStream# + * @method + */ + + /** + * Returns a promise that resolves when the stream is drained + * + * @async + * @name whenDrained + * @memberof module:scramjet.DataStream# + * @method + */ + + /** + * Returns a promise that resolves (!) when the stream is errors + * + * @async + * @name whenError + * @memberof module:scramjet.DataStream# + * @method + */ + + /** + * Allows resetting stream options. + * + * It's much easier to use this in chain than constructing new stream: + * + * ```javascript + * stream.map(myMapper).filter(myFilter).setOptions({maxParallel: 2}) + * ``` + * + * @meta.conditions keep-order,chain + * + * @memberof module:scramjet.DataStream# + * @name setOptions + * @method + * @param {DataStreamOptions} options + * @chainable + */ + + /** + * Returns a copy of the stream + * + * Creates a new stream and pushes all the data from the current one to the new one. + * This can be called serveral times. + * + * @chainable + * @param {TeeCallback|Writable} func The duplicate stream will be passed as first argument. + */ + copy() { + return this.tap().pipe(this._selfInstance()); + } + + /** + * Duplicate the stream + * + * Creates a duplicate stream instance and passes it to the Function. + * + * @chainable + * @param {TeeCallback|Writable} func The duplicate stream will be passed as first argument. + * + * @test test/methods/data-stream-tee.js + */ + tee(func) { + if (func instanceof Writable) + return (this.tap().pipe(func), this); + func(this.pipe(this._selfInstance())); + return this.tap(); + } + + /** + * @callback TeeCallback + * @memberof module:scramjet~ + * @param {DataStream} teed The teed stream + */ + + /** + * Performs an operation on every chunk, without changing the stream + * + * This is a shorthand for ```stream.on("data", func)``` but with flow control. + * Warning: this resumes the stream! + * + * @chainable + * @param {MapCallback} func a Function called for each chunk. + */ + each(func) { + return this.tap().map( + (a) => Promise.resolve(func(a)) + .then(() => a) + ).resume(); + } + + /** + * Reads the stream while the function outcome is truthy. + * + * Stops reading and emits end as soon as it finds the first chunk that evaluates + * to false. If you're processing a file until a certain point or you just need to + * confirm existence of some data, you can use it to end the stream before reaching end. + * + * Keep in mind that whatever you piped to the stream will still need to be handled. + * + * @chainable + * @param {FilterCallback} func The condition check + * + * @test test/methods/data-stream-while.js + */ + while(func) { + let condition = true; + const out = this._selfInstance(); + return this.tap().pipe(out).filter( + async (chunk) => { + const result = condition && func(chunk); + if (condition != result) { + condition = result; + this.unpipe(out); + out.write(DataStream.filter); + out.end(); + } + + return Promise + .resolve(result) + .then(result => result ? chunk : Promise.reject(DataStream.filter)); + } + ); + } + + /** + * Reads the stream until the function outcome is truthy. + * + * Works opposite of while. + * + * @chainable + * @param {FilterCallback} func The condition check + * + * @test test/methods/data-stream-until.js + */ + until(func) { + let condition = false; + const out = this._selfInstance(); + return this.tap().pipe(out).filter( + (chunk) => { + const result = condition || func(chunk); + const ref = !result ? chunk : Promise.reject(DataStream.filter); + + if (condition != result) { + condition = result; + this.unpipe(out); + out.write(DataStream.filter); + out.end(); + } + + return ref; + } + ); + } + + /** + * Provides a way to catch errors in chained streams. + * + * The handler will be called as asynchronous + * - if it resolves then the error will be muted. + * - if it rejects then the error will be passed to the next handler + * + * If no handlers will resolve the error, an `error` event will be emitted + * + * @chainable + * @name catch + * @memberof module:scramjet.DataStream# + * @method + * @param {Function} callback Error handler (async function) + * + * @test test/methods/data-stream-catch.js + */ + + /** + * Executes all error handlers and if none resolves, then emits an error. + * + * The returned promise will always be resolved even if there are no successful handlers. + * + * @async + * @name raise + * @memberof module:scramjet.DataStream# + * @method + * @param {Error} err The thrown error + * + * @test test/methods/data-stream-raise.js + */ + + /** + * Override of node.js Readable pipe. + * + * Except for calling overridden method it proxies errors to piped stream. + * + * @name pipe + * @chainable + * @ignore + * @method + * @memberof module:scramjet.DataStream# + * @param {NodeJS.WritableStream} to Writable stream to write to + * @param {WritableOptions} [options={}] + * @return {NodeJS.WritableStream} the `to` stream + */ + + /** + * Creates a BufferStream. + * + * The passed serializer must return a buffer. + * + * @meta.noReadme + * @chainable + * @param {MapCallback} serializer A method that converts chunks to buffers + * @return {BufferStream} the resulting stream + * + * @test test/methods/data-stream-tobufferstream.js + */ + bufferify(serializer) { + return this.map(serializer, scramjet.BufferStream); + } + + /** + * Creates a StringStream. + * + * The passed serializer must return a string. If no serializer is passed chunks + * toString method will be used. + * + * @chainable + * @param {MapCallback|never} [serializer] A method that converts chunks to strings + * @return {StringStream} the resulting stream + * + * @test test/methods/data-stream-tostringstream.js + */ + stringify(serializer = a => `${a}`) { + return this.map(serializer, scramjet.StringStream); + } + + /** + * Create a DataStream from an Array + * + * @param {Array<*>} array list of chunks + * @param {DataStreamOptions} [options={}] the read stream options + * @return {DataStream} + * + * @test test/methods/data-stream-fromarray.js + */ + static fromArray(array, options = {}) { + const ret = new this(options); + array = array.slice(); + array.forEach((item) => ret.write(item)); + ret.end(); + return ret; + } + + /** + * Create a DataStream from an Iterator + * + * Doesn't end the stream until it reaches end of the iterator. + * + * @param {Iterator} iterator the iterator object + * @param {DataStreamOptions} [options={}] the read stream options + * @return {DataStream} + * + * @test test/methods/data-stream-fromiterator.js + */ + static fromIterator(iterator, options) { + return new this(Object.assign({}, options, { + // TODO: handle count argument + // problem here is how do we know which promises are resolved and until where? + // need to queue a number of promises up to maxParallel, but push them with + // Promise.all with the previous one. + async parallelRead() { + const read = await iterator.next(); + if (read.done) { + return read.value ? [await read.value, null] : [null]; + } else { + return [await read.value]; + } + } + })); + } + + /** + * Aggregates the stream into a single Array + * + * In fact it's just a shorthand for reducing the stream into an Array. + * + * @async + * @param {Array} [initial=[]] Array to begin with (defaults to an empty array). + * @returns {any[]} + */ + toArray(initial = []) { + return this.reduce( + (arr, item) => (arr.push(item), arr), + initial + ); + } + + /** + * Returns an async generator + * + * @return {Generator>} Returns an iterator that returns a promise for each item. + */ + toGenerator() { + this.tap(); + const ref = this; + return function* () { + let ended = false; + ref.on("end", () => ended = true); + while (!ended) { + yield ref.whenRead(); + } + return; + }; + } + + /** + * Returns a new instance of self. + * + * Normally this doesn't have to be overridden. + * When the constructor would use some special arguments this may be used to + * override the object construction in {@link tee}... + * + * @meta.noReadme + * @memberof module:scramjet.DataStream# + * @name _selfInstance + * @method + * @return {DataStream} an empty instance of the same class. + * @test test/methods/data-stream-selfinstance.js + */ +} + +/** + * Transform async callback. The passed transform should return a new chunk, unless + * the output should be filtered - if so, the transform should return `undefined`. + * + * Additionally the function can reject with `DataStream.filter` - the result will be + * filtered and no other transforms will be run on the chunk. + * + * @callback ScramjetTransformCallback + * @memberof module:scramjet~ + * @param {Buffer|string|any} chunk the stream chunk + * @param {string} encoding encoding of the chunk + * @returns {Promise|any|undefined} the result, undefined will be treated as filtered out. + */ + +/** + * Write async callback. Await your async write and resolve. + * + * @callback ScramjetWriteCallback + * @memberof module:scramjet~ + * @param {Buffer|string|any} chunk the stream chunk + * @param {string} encoding encoding of the chunk + * @returns {Promise|void} should resolve when the write ends + */ + +/** + * Read async callback. Simply await your async operations and return the result as array. + * + * @callback ScramjetReadCallback + * @memberof module:scramjet~ + * @param {number} count the number of chunks that should be read ("this is more like a set of guideline than actual rules"). + * @returns {Array|Promise>} the read chunk. + */ + +/** + * Standard options for scramjet streams. + * + * Defines async transforms or read/write methods for a stream. + * @typedef {object} DataStreamOptions + * @memberof module:scramjet~ + * @property {ScramjetReadCallback} [promiseRead=null] an async function returning the next read item + * @property {ScramjetWriteCallback} [promiseWrite=null] an async function writing the next written item + * @property {ScramjetTransformCallback} [promiseTransform=null] an async function returning a transformed chunk + * @property {ScramjetReadCallback} [promiseFlush=null] an async function run before transform stream ends to push last chunks from the buffer + * @property {ScramjetTransformCallback} [beforeTransform=null] an async function run before the transform + * @property {ScramjetTransformCallback} [afterTransform=null] an async function run after the transform + * @property {number} [maxParallel=os.cpus.length*2] the number of transforms done in parallel + * @property {DataStream} [referrer=null] a referring stream to point to (if possible the transforms will be pushed to it + * @property {boolean} [objectMode=true] should the object mode be used instead of creating a new stream) + * @property {number} [highWaterMark] The maximum number of bytes to store in the internal buffer before ceasing to read from the underlying resource. Default: 16384 (16KB), or 16 for objectMode streams. + * @property {string} [encoding] If specified, then buffers will be decoded to strings using the specified encoding. Default: null. + * @property {boolean} [emitClose] Whether or not the stream should emit 'close' after it has been destroyed. Default: true. + * @property {Function} [read] Implementation for the stream._read() method. + * @property {Function} [destroy] Implementation for the stream._destroy() method. + * @property {Function} [construct] Implementation for the stream._construct() method. + * @property {boolean} [autoDestroy] Whether this stream should automatically call .destroy() on itself after ending. Default: true. + */ + +DataStream.prototype.toBufferStream = DataStream.prototype.bufferify; +DataStream.prototype.toStringStream = DataStream.prototype.stringify; + +module.exports = DataStream; + + +/***/ }), + +/***/ 4238: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +/** @ignore */ +const {plgctor} = __nccwpck_require__(9421); + +const registered_plugins = []; + +/** + * Scramjet main exports expose all the stream classes and a number of methods. + * + * All scramjet streams allow writing, reading or transform modes - currently + * exclusively (meaning you can't have two at once). Any of the scramjet streams + * can be constructed with the following options passed to mimic node.js standard streams: + * + * * `async promiseTransform(chunk)` - transform method that resolves with a single output chunk + * * `async promiseWrite(chunk)` - write method that that resolves when chunk is written + * * `async promiseRead(count)` - read method that resolves with an array of chunks when called + * + * See {@link https://nodejs.org/api/stream.html#stream_api_for_stream_implementers node.js API for stream implementers for details} + * + * The object exposes the following classes: + * + * * `DataStream` {@see DataStream} - the basic object stream of any type + * * `StringStream` {@see StringStream} - a stream of strings + * * `BufferStream` {@see BufferStream} - a stream of buffers + * * `MultiStream` {@see MultiStream} - a group of streams + * * `NumberStream` {@see NumberStream} - a stream of numbers + * * `WindowStream` {@see WindowStream} - a stream of windows of objects + * + * The general concept of Scramjet streams is facilitating node's TransformStream mechanism so that you don't need + * to create a number of streams and create the pipeline, but use the concept of chaining instead. When you call `parse` + * method for instance, scramjet creates a new stream, pipes it to the callee and forwards errors. + * + * What's worth mentioning - scramjet tries to limit the number of created transform streams and pushes the transforms + * one after another into the same stream class therefore a code `stream.map(transform1).map(transform2).filter(transform3)` + * will only operate on a single transform stream that evaluates all three transforms one after another. + * + * @exports scramjet + * @module scramjet + */ +module.exports = { + /** + * Creates a DataStream that's piped from the passed readable. + * + * @memberof module:scramjet + * @param {Array|Iterable|AsyncGeneratorFunction|GeneratorFunction|AsyncFunction|Function|string|Readable} input argument to be turned into new stream + * @param {DataStreamOptions|Writable} [options={}] options for creation of a new stream or the target stream + * @param {any[]} ...args additional arguments for the stream - will be passed to the function or generator + * @return {DataStream} + */ + from(...args) { + return this.DataStream.from(...args); + }, + + /** + * Creates a DataStream from an Array + * + * @memberof module:scramjet + * @param {Array} array list of chunks + * @param {DataStreamOptions} [options={}] the read stream options + * @return {DataStream} + */ + fromArray(...args) { + return this.DataStream.fromArray(...args); + }, + + get ScramjetOptions() { return __nccwpck_require__(2650); }, + + /** + * Creates a safe wrapper for scramjet transform module. See [Modules documentation](modules.md) for more info. + * + * @param {UseCallback} transform + * @param {CreateModuleOptions} [options={}] + * @param {any[]} ...initialArgs + * @return {Function} a scramjet module function + */ + createTransformModule(transform, {StreamClass = module.exports.DataStream} = {}, ...initialArgs) { + return function (stream, ...extraArgs) { + return StreamClass.from(stream) + .use(transform, ...initialArgs, ...extraArgs); + }; + }, + + /** + * Creates a safe wrapper for scramjet read module. See [Modules documentation](modules.md) for more info. + * + * @param {Array|Iterable|AsyncGeneratorFunction|GeneratorFunction|AsyncFunction|Function|string|Readable} anything + * @param {CreateModuleOptions} [options={}] + * @param {any[]} ...initialArgs + * @return {Function} a scramjet module function + */ + createReadModule(anything, {StreamClass = module.exports.DataStream} = {}, ...initialArgs) { + StreamClass = StreamClass || this.DataStream; + + return (...extraArgs) => { + return StreamClass.from(anything, ...initialArgs, ...extraArgs); + }; + }, + + /** + * Options for createModule + * + * @typedef {object} CreateModuleOptions + * @memberof module:scramjet~ + * @prop {DataStream} StreamClass defines what class should the module assume + */ + + get errors() { return __nccwpck_require__(7532); }, + + /** + * @ignore + * @see {@link buffer-stream.md} + */ + get BufferStream() { return __nccwpck_require__(2172); }, + + /** + * @ignore + * @see {@link data-stream.md} + */ + get DataStream() { return __nccwpck_require__(7248); }, + + /** + * @ignore + * @see {@link multi-stream.md} + */ + get MultiStream() { return __nccwpck_require__(2157); }, + + /** + * @ignore + * @see {@link string-stream.md} + */ + get StringStream() { return __nccwpck_require__(6428); }, + + /** + * Provides a lazy-load accessor to PromiseTransformStream - the base class of scramjet streams + * + * @ignore + */ + get PromiseTransformStream() { return (__nccwpck_require__(9421).PromiseTransformStream); }, + + /** + * Definition of a single mixin for a specific Scramjet class. Should contain any number of stream methods. + * + * @typedef {object} StreamMixin + * @memberof module:scramjet~ + * @property {Function} constructor optional constructor that will be called in the stream constructor (this has to be an own property!) + */ + + /** + * Definition of a plugin in Scramjet + * + * @typedef {object} ScramjetPlugin + * @memberof module:scramjet~ + * @internal + * @property {StreamMixin} BufferStream definition of constructor and properties for the BufferStream prototype. + * @property {StreamMixin} DataStream definition of constructor and properties for the DataStream prototype. + * @property {StreamMixin} MultiStream definition of constructor and properties for the MultiStream prototype. + * @property {StreamMixin} StringStream definition of constructor and properties for the StringStream prototype. + */ + + /** + * Plugs in methods for any of the classes + * + * @static + * @memberof module:scramjet + * @param {ScramjetPlugin} mixin the plugin object + * @return {ScramjetPlugin} + * + * @test test/methods/scramjet-plugin.js + */ + plugin(mixins) { + if (registered_plugins.includes(mixins)) { + console.log({stack: new Error().stack}); + return this; + } + registered_plugins.push(mixins); + + for (const key of Object.keys(mixins)) { + if (key in this) { + const Mixin = mixins[key]; + if (Mixin.prototype) continue; // duplicate call + + const Stream = this[key]; + if (Object.prototype.hasOwnProperty.call(Mixin, "constructor") && Stream[plgctor]) { + Stream[plgctor].ctors.push(Mixin.constructor); + delete Mixin.constructor; + } + for (let prop of Object.getOwnPropertyNames(Mixin)) { + Object.defineProperty(Stream.prototype, prop, Object.getOwnPropertyDescriptor(Mixin, prop)); + } + } else { + this[key] = mixins[key]; + } + } + return this; + }, + + /** + * Gets an API version (this may be important for future use) + * + * @static + * @memberof module:scramjet + * @param {number} version The required version (currently only: 1) + * @return {ScramjetPlugin} + */ + API(version) { + if (version === 1) { + return module.exports; + } + } +}; + +// ----- Externals documentation ----- + +/** + * Asynchronous Generator. + * + * @ignore + * @external AsyncGeneratorFunction + * @see https://github.com/tc39/proposal-async-iteration#async-generator-functions + */ + +/** + * Generator function (`function* ()`). + * + * @ignore + * @external GeneratorFunction + * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/GeneratorFunction + */ + +/** + * Simple Node.js Passthrough stream. + * + * @ignore + * @external stream.PassThrough + * @see https://nodejs.org/api/stream.html#stream_class_stream_passthrough + */ + + +/***/ }), + +/***/ 2157: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +const OUT = Symbol("OUT"); + +/** @ignore */ +const mergesortStream = __nccwpck_require__(658); +/** @ignore */ +const EventEmitter = (__nccwpck_require__(2361).EventEmitter); +/** @ignore */ +const scramjet = __nccwpck_require__(4238); +const { DataStream, PromiseTransformStream } = scramjet; + +/** + * An object consisting of multiple streams than can be refined or muxed. + * + * The idea behind a MultiStream is being able to mux and demux streams when needed. + * + * Usage: + * ```javascript + * new MultiStream([...streams]) + * .mux(); + * + * new MultiStream(function*(){ yield* streams; }) + * .map(stream => stream.filter(myFilter)) + * .mux(); + * ``` + * + * @memberof module:scramjet. + */ +class MultiStream extends EventEmitter { + + /** + * Crates an instance of MultiStream with the specified stream list + * + * @param {stream.Readable[]|AsyncGenerator|Generator} streams the list of readable streams (other objects will be filtered out!) + * @param {object} [options={}] Optional options for the super object. ;) + * + * @test test/methods/multi-stream-constructor.js + */ + constructor(streams, ...args) { + + super(args.length ? args[0] : streams); + + /** + * Array of all streams + * @type {Array} + */ + this.streams = []; + + /** + * Source of the MultiStream. + * + * This is nulled when the stream ends and is used to control the + * + * @type {DataStream} + */ + this.source = null; + + if (Array.isArray(streams)) { + streams.forEach((str) => this.add(str)); + } else if (streams) { + this.source = scramjet.DataStream + .from(streams) + .do(stream => this.add(stream)) + .run() + .then(() => this._checkEmpty(true)) + ; + } + } + + /** + * Constructs MultiStream from any number of streams-likes + * + * @param {Array|AsyncGeneratorFunction|GeneratorFunction|AsyncFunction|Function|string|Readable>} streams the array of input streamlike elements + * @param {function(new:DataStream)} [StreamClass=DataStream] + * @returns {MultiStream} + */ + static from(streams, StreamClass = DataStream) { + if (!(StreamClass.prototype instanceof PromiseTransformStream)) + throw new Error("From can instantiate stream-like classes only"); + + // We should handle non-arrays here as well + return new this(streams.map(x => StreamClass.from(x))); + } + + /** + * Returns the current stream length + * @return {number} + */ + get length() { + return this.streams.length; + } + + + /** + * @callback MultiMapCallback + * @memberof module:scramjet~ + * @async + * @param {DataStream} stream + * @returns {DataStream} + */ + + /** + * Returns new MultiStream with the streams returned by the transform. + * + * Runs a callback for every stream, returns a new MultiStream of mapped + * streams and creates a new MultiStream consisting of streams returned + * by the Function. + * + * @chainable + * @param {MultiMapCallback} aFunc Add callback (normally you need only this) + * @param {MultiMapCallback} rFunc Remove callback, called when the stream is removed + * @return {Promise} the mapped instance + * + * @test test/methods/multi-stream-map.js + */ + map(aFunc, rFunc) { + return Promise.all( + this.streams.map( + (s) => { + return Promise.resolve(s) + .then(aFunc) + ; + } + ) + ).then( + (streams) => { + const out = new (this.constructor)( + streams + ); + + this.on( + "add", + (stream) => Promise.resolve(stream) + .then(aFunc) + .then(out.add.bind(out)) + ); + + if (rFunc) + this.on( + "remove", + (stream) => Promise.resolve(stream) + .then(rFunc) + .then(out.remove.bind(out)) + ); + + return out; + } + ); + } + + /** + * Calls Array.prototype.find on the streams + * + * @param {any[]} ...args arguments for + * @return {DataStream} found DataStream + */ + find(...args) { + return this.streams.find(...args); + } + + each(aFunc, rFunc) { + return Promise.all( + this.streams.map( + (s) => { + return Promise.resolve(s) + .then(aFunc) + ; + } + ) + ).then( + () => { + this.on( + "add", + (stream) => Promise.resolve(stream).then(aFunc) + ); + + if (rFunc) + this.on( + "remove", + (stream) => Promise.resolve(stream).then(rFunc) + ); + + return this; + } + ); + } + + /** + * Filters the stream list and returns a new MultiStream with only the + * streams for which the Function returned true + * + * @chainable + * @param {Function} func Filter ran in Promise::then (so you can + * return a promise or a boolean) + * @return {MultiStream} the filtered instance + * + * @test test/methods/multi-stream-filter.js + */ + filter(func) { + return Promise.all( + this.streams.map( + (s) => Promise.resolve(s) + .then(func) + .then((o) => o ? s : null) + ) + ).then( + (streams) => { + const out = new (this.constructor)( + streams.filter((s) => s) + ); + this.on( + "add", + (stream) => Promise.resolve(stream) + .then(func) + .then(out.add.bind(out)) + ); + this.on( + "remove", out.remove.bind(out) + ); + return out; + } + ); + } + + /** + * Muxes the streams into a single one + * + * @todo For now using comparator will not affect the mergesort. + * @todo Sorting requires all the streams to be constantly flowing, any + * single one drain results in draining the muxed too even if there + * were possible data on other streams. + * + * @param {Function} [comparator] Should return -1 0 or 1 depending on the + * desired order. If passed the chunks will + * be added in a sorted order. + * @param {function(new:DataStream)} [ClassType=DataStream] the class to be outputted + * @return {DataStream} The resulting DataStream + * + * @test test/methods/multi-stream-mux.js + */ + mux(comparator = undefined, ClassType = scramjet.DataStream) { + + this[OUT] = new ClassType(); + + if (!comparator) { + + const unpipeStream = (stream) => { + if (stream) stream.unpipe(this[OUT]); + this[OUT].setMaxListeners(this.streams.length); + }; + + const pipeStream = (stream) => { + this[OUT].setMaxListeners(this.streams.length); + stream.pipe(this[OUT], {end: false}); + }; + + this.on("add", pipeStream); + this.on("remove", unpipeStream); + + this.streams.forEach(pipeStream); + + this.on("empty", () => this[OUT].end()); + + return this[OUT]; + } + + return mergesortStream(this, comparator, 0, ClassType); + } + + /** + * Adds a stream to the MultiStream + * + * If the stream was muxed, filtered or mapped, this stream will undergo the + * same transforms and conditions as if it was added in constructor. + * + * @meta.noReadme + * @param {Readable} stream [description] + * + * @test test/methods/multi-stream-add.js + */ + add(stream) { + + if (stream) { + this.streams.push(stream); + this.setMaxListeners(this.streams.length + EventEmitter.defaultMaxListeners); + this.emit("add", stream, this.streams.length - 1); + stream.on("end", () => this.remove(stream)); + } + + return this; + } + + /** + * Removes a stream from the MultiStream + * + * If the stream was muxed, filtered or mapped, it will be removed from same + * streams. + * + * @meta.noReadme + * @param {Readable} stream [description] + * + * @test test/methods/multi-stream-remove.js + */ + remove(stream) { + + const strIndex = this.streams.indexOf(stream); + if (strIndex >= 0) { + this.setMaxListeners(this.streams.length + EventEmitter.defaultMaxListeners); + this.streams.splice(strIndex, 1); + this.emit("remove", stream, strIndex); + } + + this._checkEmpty(); + + return this; + } + + _checkEmpty(ended) { + if (ended) this.source = null; + if (!this.source && !this.streams.length) this.emit("empty"); + } + +} + +module.exports = MultiStream; + + +/***/ }), + +/***/ 6428: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +/** @ignore */ +const scramjet = __nccwpck_require__(4238); + +/** @ignore */ +const SPLIT_LINE = /\r\n?|\n/g; + +/** + * A stream of string objects for further transformation on top of DataStream. + * + * Example: + * + * ```js + * StringStream.from(async () => (await fetch('https://example.com/data/article.txt')).text()) + * .lines() + * .append("\r\n") + * .pipe(fs.createWriteStream('./path/to/file.txt')) + * ``` + * @extends DataStream + * @memberof module:scramjet. + * @scope public + */ +class StringStream extends scramjet.DataStream { + + /** + * Constructs the stream with the given encoding + * + * @param {string} [encoding="utf-8"] the encoding to use + * @param {DataStreamOptions} [options={}] the encoding to use + * @return {StringStream} the created data stream + * + * @test test/methods/string-stream-constructor.js + */ + constructor(encoding, options) { + super(typeof encoding === "string" ? options : encoding); + + this.buffer = ""; + this.encoding = typeof encoding === "string" ? encoding : "utf8"; + } + + /** + * @callback ShiftStringCallback + * @memberof module:scramjet~ + * @param {string|any} shifted Shifted chars + */ + + /** + * Shifts given length of chars from the original stream + * + * Works the same way as {@see DataStream.shift}, but in this case extracts + * the given number of characters. + * + * @chainable + * @alias module:scramjet.StringStream#pop + * @alias module:scramjet.StringStream#shift + * @param {number} bytes The number of characters to shift. + * @param {ShiftStringCallback} func Function that receives a string of shifted chars. + * + * @test test/methods/string-stream-shift.js + */ + shift(bytes, func) { + const ret = ""; + const str = this.tap()._selfInstance({ + referrer: this + }); + let offs = 0; + + const chunkHandler = (chunk) => { + const length = Math.min(bytes - offs, chunk.length); + chunk.substr(0, length); + offs += length; + if (length >= bytes) { + unHook() + .then( + () => { + str.write(chunk.slice(length)); + this.pipe(str); + } + ); + } + }; + + const endHandler = (...args) => { + if (ret.length < bytes) { + unHook(); + } + str.end(...args); + }; + + const errorHandler = str.emit.bind(str, "error"); + + const unHook = () => { + this.removeListener("data", chunkHandler); + this.removeListener("end", endHandler); + this.removeListener("error", errorHandler); + return Promise.resolve(ret) + .then(func); + }; + + + this.on("data", chunkHandler); + this.on("end", endHandler); + this.on("error", errorHandler); + + return str; + } + + /** + * A handy split by line regex to quickly get a line-by-line stream + */ + static get SPLIT_LINE() { + return SPLIT_LINE; + } + + /** + * Splits the string stream by the specified RegExp or string + * + * @chainable + * @param {RegExp|string} splitter What to split by + * + * @test test/methods/string-stream-split.js + */ + split(splitter) { + if (splitter instanceof RegExp || typeof splitter === "string") { + return this.tap().pipe(this._selfInstance({ + transform(chunk, encoding, callback) { + this.buffer += chunk.toString(this.encoding); + const newChunks = this.buffer.split(splitter); + while(newChunks.length > 1) { + this.push(newChunks.shift()); + } + this.buffer = newChunks[0]; + callback(); + }, + flush(callback) { + this.push(this.buffer); + this.buffer = ""; + callback(); + }, + referrer: this + })); + } else if (splitter instanceof Function) { + return this.tap().pipe(new (this.constructor)({ + transform: splitter, + referrer: this + })); + } + } + + /** + * Finds matches in the string stream and streams the match results + * + * @chainable + * @param {RegExp} matcher A function that will be called for every + * stream chunk. + * + * @test test/methods/string-stream-match.js + */ + match(matcher) { + if (matcher instanceof RegExp) { + const replaceRegex = (matcher.source.search(/\((?!\?)/g) > -1) ? + new RegExp("[\\s\\S]*?" + matcher.source, (matcher.ignoreCase ? "i" : "") + (matcher.multiline ? "m" : "") + (matcher.unicode ? "u" : "") + "g") : + new RegExp("[\\s\\S]*?(" + matcher.source + ")", (matcher.ignoreCase ? "i" : "") + (matcher.multiline ? "m" : "") + (matcher.unicode ? "u" : "") + "g") + ; + + return this.tap().pipe(this._selfInstance({ + transform(chunk, encoding, callback) { + this.buffer = (this.buffer || "") + chunk.toString("utf-8"); + this.buffer = this.buffer.replace(replaceRegex, (...match) => { + this.push(match.slice(1, match.length - 2).join("")); + return ""; + }); + + callback(); + }, + referrer: this + })); + + } + throw new Error("Matcher must be a RegExp!"); + } + + /** + * Transforms the StringStream to BufferStream + * + * Creates a buffer stream from the given string stream. Still it returns a + * DataStream derivative and isn't the typical node.js stream so you can do + * all your transforms when you like. + * + * @meta.noReadme + * @chainable + * @return {BufferStream} The converted stream. + * + * @test test/methods/string-stream-tobufferstream.js + */ + toBufferStream() { + return this.tap().map( + (str) => Buffer.from(str, this.encoding), + new scramjet.BufferStream({ + referrer: this + }) + ); + } + + toStringStream(encoding) { + if (encoding) + return this.tap().pipe(this._selfInstance(encoding, { + referrer: this + })); + else + return this; + } + + /** + * @callback ParseCallback + * @memberof module:scramjet~ + * @param {string} chunk the transformed chunk + * @return {Promise|any} the promise should be resolved with the parsed object + */ + + /** + * Parses every string to object + * + * The method MUST parse EVERY string into a single object, so the string + * stream here should already be split. + * + * @chainable + * @param {ParseCallback} parser The transform function + * @param {function(new:DataStream)} [StreamClass] the output stream class to return + * @return {DataStream} The parsed objects stream. + * + * @test test/methods/string-stream-parse.js + */ + parse(parser, StreamClass = scramjet.DataStream) { + return this.tap().map(parser, StreamClass); + } + + /** + * Alias for {@link StringStream#parse} + * @memberof module:scramjet.StringStream# + * @method toDataStream + */ + + /** + * @meta.noReadme + * @ignore + */ + _transform(chunk, encoding, callback) { + this.push(chunk.toString(this.encoding)); + return callback(); + } + + /** + * Creates a StringStream and writes a specific string. + * + * @param {string} stream the string to push the your stream + * @param {string} encoding optional encoding + * @return {StringStream} new StringStream. + */ + static fromString(stream, encoding) { + const st = new this(encoding || "utf-8"); + st.end(stream); + return st; + } + + /** + * Creates a pipeline of streams and returns a scramjet stream. + * + * @see DataStream.pipeline + * @static + * @param {Array|Iterable|AsyncGeneratorFunction|GeneratorFunction|AsyncFunction|Function|string|Readable} readable the initial readable argument that is streamable by scramjet.from + * @param {AsyncFunction|Function|Transform} transforms Transform functions (as in {@link DataStream..use}) or Transform streams (any number of these as consecutive arguments) + * + * @returns {StringStream} a new StringStream instance of the resulting pipeline + */ + static pipeline(...args) { + return scramjet.DataStream.pipeline.call(this, ...args); + } + + /** + * Create StringStream from anything. + * + * @see DataStream.from + * @see module:scramjet.from + * + * @param {string|Array|Iterable|AsyncGeneratorFunction|GeneratorFunction|AsyncFunction|Function|Readable} source argument to be turned into new stream + * @param {DataStreamOptions|Writable} [options={}] + * @return {StringStream} new StringStream. + */ + static from(source, options, ...args) { + try { + return scramjet.DataStream.from.call(this, source, options, ...args); + } catch(e) { + if (typeof source === "string") { + return this.fromString(source); + } + throw e; + } + } + +} + +/** + * @ignore + */ +StringStream.prototype.pop = StringStream.prototype.shift; + +module.exports = StringStream; + + +/***/ }), + +/***/ 1720: +/***/ ((module) => { + +// eslint-disable-next-line node/no-unsupported-features/es-syntax +module.exports = Object.getPrototypeOf(async function*(){}).constructor; + + +/***/ }), + +/***/ 658: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +const DataStream = (__nccwpck_require__(4238).DataStream); +const DefaultBufferLength = 16; + +const wrapComparator = (comparator) => (a, b) => comparator(a[0], b[0]); +const DefaultComparator = (a, b) => { + if (a < b) return -1; + if (b < a) return 1; + return 0; +}; + +module.exports = (multi, passedComparator, bufferLength, Clazz) => { + + bufferLength = bufferLength || DefaultBufferLength; + + Clazz = Clazz || DataStream; + + const comparator = wrapComparator(passedComparator || DefaultComparator); + + const out = new Clazz(); + const readIndex = new Map(); + const endIndex = new WeakMap(); + + const rest = []; + + const onceTouchedStream = (stream) => { + return Promise.race([ + new Promise((res) => stream.on("readable", res)), + endIndex.get(stream) + ]); + }; + + const getMoreItemsForEntry = (stream, arr) => { + while (arr.length < bufferLength) { + let haveMore = stream.read(); + + if (haveMore !== null) + arr.push(haveMore); + else + break; + } + + if (arr.length || !readIndex.has(stream)) + return Promise.resolve(arr); + + return onceTouchedStream(stream) + .then( + () => getMoreItemsForEntry(stream, arr), + () => Promise.resolve(arr) + ); + }; + + const getMoreItems = () => { + const ret = []; + for (let entry of readIndex.entries()) { + ret.push(getMoreItemsForEntry(...entry)); + } + + if (!ret.length) + return Promise.resolve([]); + + return Promise.all(ret); + }; + + // TODO: rewrite as generator? + const getSorted = (inArys) => { + const arr = []; + const arys = inArys.slice(); + + let min_length = 0; + let j = 0; + + if (rest.length) { + arys.push(rest); + } + + if (!arys.length) + return []; + + while (true) { // eslint-disable-line + let cnt = 0; + + for (let ary of arys) + cnt += ary.length > j; + + if (cnt === arys.length) { + for (let i = 0; i < arys.length; i++) { + arr.push([arys[i][j], i, j, arys[i].length - j - 1]); + } + min_length = ++j; + } else { + break; + } + } + + arr.sort(comparator); + + const ret = []; + while (min_length > 0 && arr.length > 0) { + + const item = arr.shift(); + arys[item[1]].shift(item[2]); + ret.push(item[0]); + min_length = item[3]; + } + + return ret; + }; + + const writeSorted = (sorted) => { + let ret = true; + + for (var i = 0; i < sorted.length; i++) { + ret = out.write(sorted[i]); + } + + return ret || new Promise((res) => out.once("drain", () => res(sorted.end))); + }; + + let removing = null; + let pushing = null; + const pushMoreItems = () => { + + if (pushing) + return pushing; + + pushing = + getMoreItems() + .then( + (arys) => getSorted(arys) + ) + .then( + writeSorted + ) + .catch( + (e) => e instanceof Error ? out.emit("error", e) : (pushing = null, Promise.resolve()) + ) + .then( + () => { + pushing = null; + + if (readIndex.size) { + pushMoreItems(); + } else if (rest.length) { + return writeSorted(rest.sort(passedComparator)); + } + } + ); + + return pushing; + }; + + const onEmpty = () => { + return Promise.resolve(pushing) + .then(() => out.end()); + }; + + multi.each( + (addedStream) => { + const endPromise = new Promise( + (res, rej) => addedStream.on("end", () => { + multi.remove(addedStream); + rej(); + }) + ); + + endPromise.catch(() => 0); + + readIndex.set(addedStream, []); + endIndex.set( + addedStream, + endPromise + ); + }, + (removedStream) => { + removing = Promise.all([ + getMoreItemsForEntry(removedStream, readIndex.get(removedStream)) + .then( + (items) => { + readIndex.delete(removedStream); + rest.push(...items); + } + ), + removing + ]).then( + () => readIndex.size ? pushMoreItems() : onEmpty() + ); + } + ).then( + pushMoreItems + ).catch( + e => out.emit("error", e) + ); + + return out; +}; + + +/***/ }), + +/***/ 1057: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +const { StreamError } = __nccwpck_require__(7532); + +/** + * Generate read methods on the stream class. + * + * @internal + * @param {DataStreamOptions} newOptions Sanitized options passed to scramjet stream + * @return {Boolean} returns true if creation of new stream is not necessary (promise can be pushed to queue) + */ +module.exports = () => function mkRead(newOptions) { + this.setOptions( + { + // transforms: [], + promiseRead: newOptions.promiseRead + } + ); + + let chunks = []; + let done = false; + // TODO: implement the actual parallel logic - items can be promises and should be flushed when resolved. + const pushSome = () => Array.prototype.findIndex.call(chunks, chunk => { + return !this.push(chunk); + }) + 1; + + // let last = Promise.resolve(); + // let processing = []; + + this.on("pipe", () => { + throw new Error("Cannot pipe to a Readable stream"); + }); + + this._read = async (size) => { + try { + let add = 0; + if (!done) { + const nw = await this._options.promiseRead(size); + chunks.push(...nw); + add = nw.length; + } + const pushed = pushSome(); + chunks = chunks.slice(pushed || Infinity); + + // Yes, it could be reassigned, but only with true so we don't need to care + // eslint-disable-next-line require-atomic-updates + done = done || !add; + + if (done && !chunks.length) { + await new Promise((res, rej) => this._flush((err) => err ? rej(err) : res())); + this.push(null); + } + + // console.log("read", pushed, chunks, add, size); + // TODO: check for existence of transforms and push to transform directly. + // TODO: but in both cases transform methods must be there... which aren't there now. + // TODO: at least the subset that makes the transform - yes, otherwise all that transform stuff + // TODO: is useless and can be bypassed... + } catch(e) { + await this.raise(new StreamError(e, this)); + return this._read(size); + } + }; + +}; + + +/***/ }), + +/***/ 7600: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +const ignore = () => 0; +const { StreamError } = __nccwpck_require__(7532); + + +/** + * Generate transform methods on the stream class. + * + * @internal + * @memberof PromiseTransformStream + * @param {DataStreamOptions} newOptions Sanitized options passed to scramjet stream + * @return {Boolean} returns true if creation of new stream is not necessary (promise can be pushed to queue) + */ +module.exports = ({filter}) => function mkTransform(newOptions) { + this.setOptions( + { + transforms: [], + beforeTransform: newOptions.beforeTransform, + afterTransform: newOptions.afterTransform, + promiseFlush: newOptions.promiseFlush + } + ); + + this.cork(); + if (newOptions.referrer instanceof this.constructor && !newOptions.referrer._tapped && !newOptions.referrer._options.promiseFlush) { + return true; + } + + process.nextTick(this.uncork.bind(this)); + + this.pushTransform(newOptions); + + if (this._scramjet_options.transforms.length) { + + const processing = []; + let last = Promise.resolve(); + + this._transform = (chunk, encoding, callback) => { + if (!this._scramjet_options.transforms.length) { + return last.then( + () => callback(null, chunk) + ); + } + + const prev = last; + const ref = last = Promise + .all([ + this._scramjet_options.transforms.reduce( + (prev, transform) => prev.then(transform), + Promise.resolve(chunk) + ).catch( + (err) => err === filter ? filter : Promise.reject(err) + ), + prev + ]) + .catch( + async (e) => { + if (e instanceof Error) { + return Promise.all([ + this.raise(new StreamError(e, this, "EXTERNAL", chunk), chunk), + prev + ]); + } else { + throw new Error("New stream error raised without cause!"); + } + } + ) + .then( + (args) => { + if (args && args[0] !== filter && typeof args[0] !== "undefined") { + try { + this.push(args[0]); + } catch(e) { + return this.raise(new StreamError(e, this, "INTERNAL", chunk), chunk); + } + } + } + ); + + processing.push(ref); // append item to queue + if (processing.length >= this._options.maxParallel) { + processing[processing.length - this._options.maxParallel] + .then(() => callback()) + .catch(ignore); + } else { + callback(); + } + + ref.then( + () => { + const next = processing.shift(); + return ref !== next && this.raise(new StreamError(new Error(`Promise resolved out of sequence in ${this.name}!`), this, "TRANSFORM_OUT_OF_SEQ", chunk), chunk); + } + ); + + }; + + this._flush = (callback) => { + if (this._scramjet_options.runFlush) { + last + .then(this._scramjet_options.runFlush) + .then( + (data) => { + if (Array.isArray(data)) + data.forEach(item => this.push(item)); + else if (data) + this.push(data); + }, + e => this.raise(e) + ) + .then(() => callback()); + } else { + last.then(() => callback()); + } + }; + } +}; + + +/***/ }), + +/***/ 7087: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +const { StreamError } = __nccwpck_require__(7532); + +/** + * Generate write methods on the stream class. + * + * @internal + * @param {DataStreamOptions} newOptions Sanitized options passed to scramjet stream + * @return {Boolean} returns true if creation of new stream is not necessary (promise can be pushed to queue) + */ +module.exports = () => function mkWrite(newOptions) { + this.tap().setOptions( + { + // transforms: [], + promiseWrite: newOptions.promiseWrite + } + ); + + this.pipe = () => { + throw new Error("Method not allowed on a Writable only stream"); + }; + + this._write = (chunk, encoding, callback) => { + Promise.resolve(chunk) + .then((chunk) => this._options.promiseWrite(chunk, encoding)) + .then(() => callback()) + .catch( + (e) => this.raise(new StreamError(e, this, "EXTERNAL", chunk), chunk) + ); + }; + +}; + + +/***/ }), + +/***/ 2650: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +const { EventEmitter } = __nccwpck_require__(2361); +const _declarations = Symbol("declarations"); +const _values = Symbol("values"); +const _chain = Symbol("chain"); +const _owner = Symbol("owner"); + +const isClass = cls => cls && typeof cls === "function" && cls.prototype.constructor === cls && cls !== Object; +const _superClass = (cls) => { + return isClass(cls) && cls.prototype.__proto__ && cls.prototype.__proto__.constructor; +}; + +const inheritedProxy = (parent) => { + return new Proxy({}, { + has(target, prop) { + return prop in target || parent && prop in parent; + }, + get(target, prop) { + if (prop in target) + return target[prop]; + + return parent && parent[prop]; + }, + set(target, prop, value) { + if (prop in target) + throw new Error(`Option "${prop}" already defined`); + + target[prop] = value; + return true; + } + }); +}; + +const findProxyForInstance = (ref, instance) => { + return findProxyForClass(ref.__proto__.constructor[_declarations], instance.__proto__.constructor); +}; + +const findProxyForClass = (map, cls) => { + if (!isClass(cls)) { + throw new Error("Cannot get options declaration for a non-constructor"); + } + + if (map.has(cls)) + return map.get(cls); + + const superClass = _superClass(cls); + const parent = isClass(superClass) ? findProxyForClass(map, superClass) : null; + + const declaration = inheritedProxy(parent); + map.set(cls, declaration); + + return declaration; +}; + +const DefaultDefinition = {}; + +/** + * Scramjet options for streams + */ +module.exports = class ScramjetOptions extends EventEmitter { + /** + * + * @param {PromiseTransformStream} owner + * @param {ScramjetOptions} chain + */ + constructor(owner, chain, initial) { + super(); + this[_owner] = owner; + this[_chain] = chain; + this[_declarations] = findProxyForInstance(this, owner); + this[_values] = {}; + Object.assign(this[_values], initial); + } + + get proxy() { + const value = new Proxy(this, { + has(target, key) { + if (!(key in target[_declarations])) + return false; + return key in target[_values] || target[_declarations][key].value; + }, + ownKeys(target) { + return Object.keys(target[_values]); + }, + getOwnPropertyDescriptor(target, key) { + if (key in target[_values]) { + return { + value: this.get(target, key), + enumerable: true, + configurable: true, + writable: true + }; + } + }, + get(target, key) { + if (key in target[_declarations]) { + if (key in target[_values]) + return target[_values][key]; + + if (target[_chain] && target[_declarations][key].chained && Object.prototype.hasOwnProperty.call(target[_chain], key)) + return target[_chain][key]; + + return target[_declarations][key].value; + } + + throw new Error(`Attempting to access undeclared option: ${key}`); + }, + set(target, key, value) { + if (key in target[_declarations]) { + target[_values][key] = value; + return; + } + + throw new Error(`Attempting to set undeclared option: ${key}`); + } + }); + + Object.defineProperty(this, "proxy", { value }); + return value; + } + + /** + * Declares a usable option. + * + * @param {Function} cls + * @param {string} name + * @param {object} definition + */ + static declare(cls, name, { chained = false, value } = DefaultDefinition) { + this[_declarations] = this[_declarations] || new Map(); + const optionsList = findProxyForClass(this[_declarations], cls); + + optionsList[name] = { chained, value }; + } +}; + + +/***/ }), + +/***/ 9421: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +const {Transform, Readable} = __nccwpck_require__(2781); +const {EventEmitter} = __nccwpck_require__(2361); +const DefaultHighWaterMark = (__nccwpck_require__(2037).cpus)().length * 2; + +const filter = Symbol("FILTER"); +const plgctor = Symbol("plgctor"); +const storector = Symbol("storector"); + +let seq = 0; + +const shared = { filter, DefaultHighWaterMark, plgctor, storector }; +const mkTransform = __nccwpck_require__(7600)(shared); +const mkRead = __nccwpck_require__(1057)(shared); +const mkWrite = __nccwpck_require__(7087)(shared); +const {StreamError} = __nccwpck_require__(7532); + +const rename = (ob, fr, to) => { + if (ob[fr]) { + ob[to] = ob[fr]; + delete ob[fr]; + } +}; + +const checkOptions = (options) => { + rename(options, "parallelRead", "promiseRead"); + rename(options, "parallelWrite", "promiseWrite"); + rename(options, "parallelTransform", "promiseTransform"); + rename(options, "flushPromise", "promiseFlush"); + + if (["promiseRead", "promiseWrite", "promiseTransform"].reduce((acc, key) => acc += (options[key] ? 1 : 0), 0) > 1) + throw new Error("Scramjet stream can be either Read, Write or Transform"); +}; + +/** + * This class is an underlying class for all Scramjet streams. + * + * It allows creation of simple transform streams that use async functions for transforms, reading or writing. + * + * @internal + * @extends stream.PassThrough + */ +class PromiseTransformStream extends Transform { + + constructor(options) { + options = options || {}; + const newOptions = Object.assign({ + objectMode: true, + promiseRead: null, + promiseWrite: null, + promiseTransform: null, + promiseFlush: null, + beforeTransform: null, + afterTransform: null + }, options); + + checkOptions(newOptions); + + super(newOptions); + + this._tapped = false; + + this._error_handlers = []; + this._scramjet_options = { + referrer: options.referrer, + constructed: (new Error().stack) + }; + + this.seq = seq++; + + this.setMaxListeners(DefaultHighWaterMark); + this.setOptions(newOptions); + + if (newOptions.promiseRead) { + this.type = "Read"; + mkRead.call(this, newOptions); + this.tap(); + } else if (newOptions.promiseWrite) { + this.type = "Write"; + mkWrite.call(this, newOptions); + } else if (newOptions.transform || !newOptions.promiseTransform) { + this.type = "Transform-"; + this.tap(); + } else { + this.type = "Transform"; + if (newOptions.promiseTransform && mkTransform.call(this, newOptions)) { // returns true if transform can be pushed to referring stream + return options.referrer.pushTransform(options); + } + } + + const pluginConstructors = this.constructor[plgctor].get(); + if (pluginConstructors.length) { + + let ret; + pluginConstructors.find( + (Ctor) => ret = Ctor.call(this, options) + ); + + if (typeof ret !== "undefined") { + return ret; + } + } + } + + get name() { + return `${this.constructor.name}(${this._options.name || this.seq})`; + } + + set name(name) { + this.setOptions({name}); + } + + get constructed() { + return this._scramjet_options.constructed; + } + + get _options() { + if (this._scramjet_options.referrer && this._scramjet_options.referrer !== this) { + return Object.assign({maxParallel: DefaultHighWaterMark}, this._scramjet_options.referrer._options, this._scramjet_options); + } + return Object.assign({maxParallel: DefaultHighWaterMark}, this._scramjet_options); + } + + setOptions(...options) { + Object.assign(this._scramjet_options, ...options); + + if (this._scramjet_options.maxParallel) + this.setMaxListeners(this._scramjet_options.maxParallel); + + if (this._flushed) { + options.forEach( + ({promiseFlush}) => Promise + .resolve() + .then(promiseFlush) + .catch(e => this.raise(e)) + ); + } + + return this; + } + + setMaxListeners(value) { + return super.setMaxListeners.call(this, value + EventEmitter.defaultMaxListeners); + } + + static get [plgctor]() { + const proto = Object.getPrototypeOf(this); + return { + ctors: this[storector] = Object.prototype.hasOwnProperty.call(this, storector) ? this[storector] : [], + get: () => proto[plgctor] ? proto[plgctor].get().concat(this[storector]) : this[storector] + }; + } + + async whenRead(count) { + return Promise.race([ + new Promise((res) => { + + const read = () => { + const ret = this.read(count); + if (ret !== null) { + return res(ret); + } else { + this.once("readable", read); + } + }; + read(); + }), + this.whenError(), + this.whenEnd() + ]); + } + + async whenDrained() { + return this._scramjet_drainPromise || (this._scramjet_drainPromise = new Promise( + (res, rej) => this + .once("drain", () => { + this._scramjet_drainPromise = null; + res(); + }) + .whenError().then(rej) + )); + } + + async whenWrote(...data) { + let ret; + for (var item of data) + ret = this.write(item); + + if (ret) { + return; + } else { + return this.whenDrained(); + } + } + + async whenError() { + return this._scramjet_errPromise || (this._scramjet_errPromise = new Promise((res) => { + this.once("error", (e) => { + this._scramjet_errPromise = null; + res(e); + }); + })); + } + + async whenEnd() { + return this._scramjet_endPromise || (this._scramjet_endPromise = new Promise((res, rej) => { + this.whenError().then(rej); + this.on("end", () => res()); + })); + } + + async whenFinished() { + return this._scramjet_finishPromise || (this._scramjet_finishPromise = new Promise((res, rej) => { + this.whenError().then(rej); + this.on("finish", () => res()); + })); + } + + catch(callback) { + this._error_handlers.push(callback); + return this; + } + + async raise(err, ...args) { + return this._error_handlers + .reduce( + (promise, handler) => promise.catch( + (lastError) => handler( + lastError instanceof StreamError + ? lastError + : new StreamError(lastError, this, err.code, err.chunk), + ...args + ) + ), + Promise.reject(err) + ) + .catch( + (err) => this.emit("error", err, ...args) + ); + } + + pipe(to, options) { + if (to === this) { + return this; + } + + if (this !== to && to instanceof PromiseTransformStream) { + to.setOptions({referrer: this}); + this.on("error", err => to.raise(err)); + this.tap().catch(async (err, ...args) => { + await to.raise(err, ...args); + return filter; + }); + } else if (to instanceof Readable) { + this.on("error", (...err) => to.emit("error", ...err)); + } + + return super.pipe(to, options || {end: true}); + } + + graph(func) { + let referrer = this; + const ret = []; + while(referrer) { + ret.push(referrer); + referrer = referrer._options.referrer; + } + func(ret); + return this; + } + + tap() { + this._tapped = true; + return this; + } + + dropTransform(transform) { + if (!this._scramjet_options.transforms) { + if (!this._transform.currentTransform) + return this; + + this._transform = this._transform.currentTransform; + return this; + } + let i = 0; + while (i++ < 1000) { + const x = this._scramjet_options.transforms.findIndex(t => t.ref === transform); + if (x > -1) { + this._scramjet_options.transforms.splice(x, 1); + } else { + return this; + } + } + throw new Error("Maximum remove attempt count reached!"); + } + + pushTransform(options) { + + if (typeof options.promiseTransform === "function") { + if (!this._scramjet_options.transforms) { + this._pushedTransform = options.promiseTransform; + return this; + } + + const markTransform = (bound) => { + bound.ref = options.promiseTransform; + return bound; + }; + + const before = typeof options.beforeTransform === "function"; + const after = typeof options.afterTransform === "function"; + + if (before) + this._scramjet_options.transforms.push(markTransform( + options.beforeTransform.bind(this) + )); + + if (after) + this._scramjet_options.transforms.push(markTransform( + async (chunk) => options.afterTransform.call( + this, + chunk, + await options.promiseTransform.call(this, chunk) + ) + )); + else + this._scramjet_options.transforms.push(markTransform( + options.promiseTransform.bind(this) + )); + + } + + if (typeof options.promiseFlush === "function") { + if (this._scramjet_options.runFlush) { + throw new Error("Promised Flush cannot be overwritten!"); + } else { + this._scramjet_options.runFlush = options.promiseFlush; + } + } + + return this; + } + + _selfInstance(...args) { + return new this.constructor(...args); + } + + async _transform(chunk, encoding, callback) { + if (!this._delayed_first) { + await new Promise(res => res()); + this._delayed_first = 1; + } + + try { + if (this._pushedTransform) + chunk = await this._pushedTransform(chunk); + callback(null, chunk); + } catch(err) { + callback(err); + } + } + + _flush(callback) { + const last = Promise.resolve(); + + if (this._scramjet_options.runFlush) { + last + .then(this._scramjet_options.runFlush) + .then( + (data) => { + if (Array.isArray(data)) + data.forEach(item => this.push(item)); + else if (data) + this.push(data); + + callback(); + }, + e => this.raise(e) + ); + } else { + last.then(() => callback()); + } + } + + static get filter() { return filter; } +} + +module.exports = { + plgctor: plgctor, + PromiseTransformStream +}; + + +/***/ }), + +/***/ 7532: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +const os = __nccwpck_require__(2037); +const combineStack = (stack, ...errors) => { + return errors.reduce( + (stack, trace) => { + if (!trace) return stack; + if (trace.indexOf("\n") >= 0) + return stack + os.EOL + trace.substr(trace.indexOf("\n") + 1); + + else + return stack + os.EOL + trace; + }, + stack + ); +}; + +class StreamError extends Error { + + constructor(cause, stream, code = "GENERAL", chunk = null) { + code = cause.code || code; + stream = cause.stream || stream; + chunk = cause.chunk || chunk; + + super(cause.message); + + if (cause instanceof StreamError) + return cause; + + this.chunk = chunk; + this.stream = stream; + this.code = "ERR_SCRAMJET_" + code; + this.cause = cause; + + const stack = this.stack; + Object.defineProperty(this, "stack", { + get: function () { + return combineStack( + stack, + " caused by:", + cause.stack, + ` --- raised in ${stream.name} constructed ---`, + stream.constructed + ); + } + }); + + /** Needed to fix babel errors. */ + this.constructor = StreamError; + this.__proto__ = StreamError.prototype; + } + +} + +/** + * Stream errors class + * + * @module scramjet/errors + * @prop {Class} StreamError + * @prop {Function} combineStack + */ +module.exports = {StreamError, combineStack}; + + +/***/ }), + +/***/ 8603: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +const {dirname, resolve} = __nccwpck_require__(1017); + +/** @ignore */ +const getCalleeDirname = function(depth) { + const p = Error.prepareStackTrace; + Error.prepareStackTrace = (dummy, stack) => stack; + const e = new Error(); + Error.captureStackTrace(e, arguments.callee); + const stack = e.stack; + Error.prepareStackTrace = p; + return dirname(stack[depth].getFileName()); +}; + +const resolveCalleeRelative = function(depth, ...relatives) { + return resolve(getCalleeDirname(depth + 1), ...relatives); +}; + +/** @ignore */ +const resolveCalleeBlackboxed = function() { + const p = Error.prepareStackTrace; + Error.prepareStackTrace = (dummy, stack) => stack; + const e = new Error(); + Error.captureStackTrace(e, arguments.callee); + const stack = e.stack; + Error.prepareStackTrace = p; + + let pos = stack.find(entry => entry.getFileName().indexOf(resolve(__dirname, "..")) === -1); + + return resolve(dirname(pos.getFileName()), ...arguments); +}; + +/** + * @external AsyncGeneratorFunction + */ +let AsyncGeneratorFunction = function() {}; +try { + AsyncGeneratorFunction = __nccwpck_require__(1720); +} catch (e) {} // eslint-disable-line + +/** + * @external GeneratorFunction + */ +const GeneratorFunction = Object.getPrototypeOf(function*(){}).constructor; + +/** @ignore */ +const pipeIfTarget = (stream, target) => (target ? stream.pipe(target) : stream); + +/** @ignore */ +const pipeThen = async (func, target) => Promise + .resolve() + .then(func) + .then(x => x.pipe(target)) + .catch(e => target.raise(e)); + +/** + * @external stream.PassThrough + * @see https://nodejs.org/api/stream.html#stream_class_stream_passthrough + */ + +module.exports = { + AsyncGeneratorFunction, + GeneratorFunction, + getCalleeDirname, + resolveCalleeRelative, + resolveCalleeBlackboxed, + pipeIfTarget, + pipeThen +}; + + +/***/ }), + +/***/ 3946: +/***/ ((module) => { + +module.exports = {}; + + +/***/ }), + +/***/ 203: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +module.exports = __nccwpck_require__(4238); + + +/***/ }), + +/***/ 9738: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +const scramjet = __nccwpck_require__(203); +const { PromiseTransformStream, StringStream, DataStream, MultiStream } = scramjet; +const { EventEmitter } = __nccwpck_require__(2361); +const { ReReadable } = __nccwpck_require__(2582); +const { AsyncGeneratorFunction, GeneratorFunction } = __nccwpck_require__(8603); + +const os = __nccwpck_require__(2037); +const path = __nccwpck_require__(1017); + +/** @ignore */ +const {getCalleeDirname} = __nccwpck_require__(8603); + +module.exports = { + + constructor() { + this.TimeSource = Date; + this.setTimeout = setTimeout; + this.clearTimeout = clearTimeout; + + this.buffer = null; + }, + + /** + * Pulls in any readable stream, resolves when the pulled stream ends. + * + * You can also pass anything that can be passed to `DataStream.from`. + * + * Does not preserve order, does not end this stream. + * + * @async + * @memberof module:scramjet.DataStream# + * @param {Array|Iterable|AsyncGeneratorFunction|GeneratorFunction|AsyncFunction|Function|string|Readable} pullable + * @param {any[]} ...args any additional args + * @returns {Promise} resolved when incoming stream ends, rejects on incoming error + * + * @test test/methods/data-stream-pull.js + */ + async pull(pullable, ...args) { + return new Promise((res, rej) => { + const incoming = this.constructor.from(pullable, {}, ...args); + + incoming.pipe(this, { end: false }); + incoming.on("end", res); + incoming.on("error", rej); + }); + }, + + /** + * Shift Function + * + * @callback ShiftCallback + * @memberof module:scramjet~ + * @param {Array|any} shifted an array of shifted chunks + */ + + /** + * Shifts the first n items from the stream and pushes out the remaining ones. + * + * @chainable + * @memberof module:scramjet.DataStream# + * @param {number} count The number of items to shift. + * @param {ShiftCallback} func Function that receives an array of shifted items + * + * @test test/methods/data-stream-shift.js + */ + shift(count, func) { + const ret = []; + const str = this.tap()._selfInstance({referrer: this}); + + const chunkHandler = (chunk) => { + ret.push(chunk); + if (ret.length >= count) { + this.pause(); + unHook().then( + () => this.resume().pipe(str) + ); + } + }; + + const endHandler = (...args) => { + unHook().then( + () => str.end(...args) + ); + }; + + const errorHandler = str.emit.bind(str, "error"); + + let hooked = true; + const unHook = () => { + if (hooked) { + hooked = false; + this.removeListener("data", chunkHandler); + this.removeListener("end", endHandler); + this.removeListener("error", errorHandler); + } + return Promise.resolve(ret) + .then(func); + }; + + this.on("data", chunkHandler); + this.on("end", endHandler); + this.on("error", errorHandler); + + return str; + }, + + /** + * Allows previewing some of the streams data without removing them from the stream. + * + * Important: Peek does not resume the flow. + * + * @memberof module:scramjet.DataStream# + * @param {number} count The number of items to view before + * @param {ShiftCallback} func Function called before other streams + * @chainable + */ + peek(count, func) { + const ref = this._selfInstance({referrer: this}); + + this + .tap() + .pipe(ref) + .shift(count, batch => { + this.unpipe(ref); + return func(batch); + }); + + return this; + }, + + /** + * Slices out a part of the stream to the passed Function. + * + * Returns a stream consisting of an array of items with `0` to `start` + * omitted and `length` items after `start` included. Works similarly to + * Array.prototype.slice. + * + * Takes count from the moment it's called. Any previous items will not be + * taken into account. + * + * @chainable + * @memberof module:scramjet.DataStream# + * @param {number} [start=0] omit this number of entries. + * @param {number} [length=Infinity] get this number of entries to the resulting stream + * + * @test test/methods/data-stream-slice.js + */ + slice(start = 0, length = Infinity) { + let n = 0; + + let stream = this; + if (start > 0) { + stream = this.shift(start, () => 0); + } + + if (length === Infinity) { + return this; + } + + return stream.until(() => n++ >= length); + }, + + /** + * Transforms stream objects by assigning the properties from the returned + * data along with data from original ones. + * + * The original objects are unaltered. + * + * @chainable + * @memberof module:scramjet.DataStream# + * @param {MapCallback|object} func The function that returns new object properties or just the new properties + * + * @test test/methods/data-stream-assign.js + */ + assign(func) { + if (typeof func === "function") { + return this.map( + (chunk) => Promise.resolve(func(chunk)) + .then(obj => Object.assign({}, chunk, obj)) + ); + } else { + return this.map( + (chunk) => Object.assign({}, chunk, func) + ); + } + }, + + /** + * Called only before the stream ends without passing any items + * + * @chainable + * @memberof module:scramjet.DataStream# + * @param {Function} callback Function called when stream ends + * + * @test test/methods/data-stream-empty.js + */ + empty(callback) { + let z = false; + const promiseTransform = () => { + z = true; + this.dropTransform(promiseTransform); + }; + + this.pushTransform({promiseTransform}) + .tap() + .whenEnd() + .then( + () => (z || Promise.resolve().then(callback)), + () => 0 + ); + + return this; + }, + + + /** + * Pushes any data at call time (essentially at the beginning of the stream) + * + * This is a synchronous only function. + * + * @chainable + * @memberof module:scramjet.DataStream# + * @param {any[]} ...item list of items to unshift (you can pass more items) + */ + unshift(...items) { + items.forEach( + item => this.write(item) + ); + return this.tap(); + }, + + /** + * Pushes any data at end of stream + * + * @chainable + * @memberof module:scramjet.DataStream# + * @param {*} item list of items to push at end + * @meta.noreadme + * + * @test test/methods/data-stream-endwith.js + */ + endWith(...items) { + // TODO: overhead on unneeded transform, but requires changes in core. + // TODO: should accept similar args as `from` + return this.pipe(this._selfInstance({ + referrer: this, + promiseTransform: (a) => a, + flushPromise: () => items + })); + }, + + /** + * Accumulates data into the object. + * + * Works very similarly to reduce, but result of previous operations have + * no influence over the accumulator in the next one. + * + * Method works in parallel. + * + * @async + * @memberof module:scramjet.DataStream# + * @param {AccumulateCallback} func The accumulation function + * @param {*} into Accumulator object + * @return {Promise} resolved with the "into" object on stream end. + * @meta.noreadme + * + * @test test/methods/data-stream-accumulate.js + */ + async accumulate(func, into) { + return new Promise((res, rej) => { + const bound = async (chunk) => (await func(into, chunk), Promise.reject(DataStream.filter)); + bound.to = func; + + this.tap().pipe(new PromiseTransformStream({ + promiseTransform: bound, + referrer: this + })) + .on("end", () => res(into)) + .on("error", rej) + .resume(); + }); + }, + + /** + * @callback AccumulateCallback + * @memberof module:scramjet~ + * @param {*} accumulator Accumulator passed to accumulate function + * @param {*} chunk the stream chunk + * @return {Promise|*} resolved when all operations are completed + */ + + /** + * Consumes the stream by running each Function + * + * @deprecated use {@link DataStream#each} instead + * + * @async + * @memberof module:scramjet.DataStream# + * @param {ConsumeCallback|AsyncGeneratorFunction|GeneratorFunction} func the consument + * @param {any[]} ...args additional args will be passed to generators + * @meta.noreadme + */ + async consume(func, ...args) { + let runFunc = func; + if (func instanceof GeneratorFunction || func instanceof AsyncGeneratorFunction) { + const gen = await func(...args); + await gen.next(); + runFunc = async item => { + await gen.next(item); + }; + } + + return this.tap() + .do(runFunc) + .resume() + .whenEnd(); + }, + + /** + * @callback ConsumeCallback + * @memberof module:scramjet~ + * @param {*} chunk the stream chunk + * @return {Promise|*} resolved when all operations are completed + */ + + /** + * Reduces the stream into the given object, returning it immediately. + * + * The main difference to reduce is that only the first object will be + * returned at once (however the method will be called with the previous + * entry). + * If the object is an instance of EventEmitter then it will propagate the + * error from the previous stream. + * + * This method is serial - meaning that any processing on an entry will + * occur only after the previous entry is fully processed. This does mean + * it's much slower than parallel functions. + * + * @meta.noreadme + * @chainable + * @memberof module:scramjet.DataStream# + * @param {ReduceCallback} func The into object will be passed as the first argument, the data object from the stream as the second. + * @param {*|EventEmitter} into Any object passed initially to the transform function + * @return {*} whatever was passed as into + * + * @test test/methods/data-stream-reduceNow.js + */ + reduceNow(func, into) { + const prm = this.reduce(func, into); + + if (into instanceof EventEmitter) { + prm.catch((e) => into.emit("error", e)); + } + + return into; + }, + + /** + * @callback RemapCallback + * @memberof module:scramjet~ + * @param {Function} emit a method to emit objects in the remapped stream + * @param {*} chunk the chunk from the original stream + * @returns {Promise|*} promise to be resolved when chunk has been processed + */ + + /** + * Remaps the stream into a new stream. + * + * This means that every item may emit as many other items as we like. + * + * @meta.noreadme + * @chainable + * @memberof module:scramjet.DataStream# + * @param {RemapCallback} func A Function that is called on every chunk + * @param {function(new:DataStream)} [ClassType=this.constructor] Optional DataStream subclass to be constructed + * + * @test test/methods/data-stream-remap.js + */ + remap(func, ClassType = this.constructor) { + + const ref = new (ClassType || this.constructor)({referrer: this}); + + return this.into( + async (str, chunk) => { + let out = []; + await func((newChunk) => out.push(newChunk), chunk); + + let last = true; + for (const val of out) + last = ref.write(val); + + return last ? null : ref.whenDrained(); + }, + ref + ); + }, + + /** + * Takes any method that returns any iterable and flattens the result. + * + * The passed Function must return an iterable (otherwise an error will be emitted). The resulting stream will + * consist of all the items of the returned iterables, one iterable after another. + * + * @chainable + * @memberof module:scramjet.DataStream# + * @param {FlatMapCallback} func A Function that is called on every chunk + * @param {function(new:DataStream)} [ClassType=this.constructor] Optional DataStream subclass to be constructed + * @param {any[]} ...args additional args will be passed to generators + * + * @test test/methods/data-stream-flatmap.js + */ + flatMap(func, ClassType, ...args) { + if (typeof ClassType !== "function") + ClassType = this.constructor; + + const ref = new ClassType({referrer: this}); + const asyncIteratorSymbol = Symbol.asyncIterator; + + return this.into( + async (ref, chunk) => { + const out = await func(chunk, ...args); + if (!out) { + throw new Error("Non iterable object returned for flatMap!"); + } + + if (asyncIteratorSymbol && out[asyncIteratorSymbol]) { + const iterator = out[asyncIteratorSymbol](); + // eslint-disable-next-line no-constant-condition + while (true) { + const item = await iterator.next(); + if (item.done) return; + if (!ref.write(item.value)) await ref.whenDrained(); + } + } else { + const chunks = [...out]; + if (chunks.length === 0) return; + await ref.whenWrote(...chunks); + } + }, + ref + ); + }, + + /** + * @callback FlatMapCallback + * @memberof module:scramjet~ + * @param {*} chunk the chunk from the original stream + * @returns {AsyncGenerator|Promise>|Iterable} promise to be resolved when chunk has been processed + */ + + /** + * A shorthand for streams of arrays or iterables to flatten them. + * + * More efficient equivalent of: `.flatmap(i => i);` + * Works on streams of async iterables too. + * + * @chainable + * @memberof module:scramjet.DataStream# + * @return {DataStream} + * + * @test test/methods/data-stream-flatten.js + */ + flatten() { + const iteratorSymbol = Symbol.asyncIterator; + let wroteAll = Promise.resolve(); + + return this.into( + async (ref, chunk) => { + const prevWrote = wroteAll; + let res; + wroteAll = new Promise(_res => res = _res); + + if (iteratorSymbol && chunk[iteratorSymbol]) { + const iterator = chunk[iteratorSymbol](); + await prevWrote; + // eslint-disable-next-line no-constant-condition + while (true) { + const item = await iterator.next(); + + if (item.value && !ref.write(item.value)) await ref.whenDrained(); + if (item.done) return res(); + } + } else { + let last = true; + await prevWrote; + for (const val of chunk) { + last = ref.write(val); + if (!last) await ref.whenDrained(); + } + return res(); + } + }, + this._selfInstance() + ); + }, + + /** + * Returns a new stream that will append the passed streams to the callee + * + * @chainable + * @memberof module:scramjet.DataStream# + * @param {Readable[]} ...streams Streams to be injected into the current stream + * + * @test test/methods/data-stream-concat.js + */ + concat(...streams) { + const out = this._selfInstance({referrer: this}); + + streams.unshift(this); + + const next = () => { + if (streams.length) + streams.shift() + .on("end", next) + .pipe(out, {end: !streams.length}); + }; + next(); + + return out; + }, + + /** + * @callback JoinCallback + * @memberof module:scramjet~ + * @param {*} previous the chunk before + * @param {*} next the chunk after + * @returns {Promise<*>|*} promise that is resolved with the joining item + */ + + /** + * Method will put the passed object between items. It can also be a function call or generator / iterator. + * + * If a generator or iterator is passed, when the iteration is done no items will be interweaved. + * Generator receives + * + * @chainable + * @memberof module:scramjet.DataStream# + * @param {*|AsyncGeneratorFunction|GeneratorFunction|JoinCallback} item An object that should be interweaved between stream items + * @param {any[]} ...args additional args will be passed to generators + * + * @test test/methods/data-stream-join.js + */ + join(item, ...args) { + const ref = this._selfInstance({referrer: this}); + + let prev; + let consumer; + if (typeof item !== "function") { + consumer = (cur) => Promise.all([ + ref.whenWrote(item), + ref.whenWrote(cur) + ]); + } else if (item instanceof GeneratorFunction || item instanceof AsyncGeneratorFunction) { + const iterator = item(...args); + consumer = cur => Promise.resolve() + .then(() => iterator.next(prev)) + .then(({value, done}) => Promise.all([ + !done && ref.whenWrote(value), + ref.whenWrote(prev = cur) + ])) + ; + } else { + consumer = cur => + Promise.resolve([prev, prev = cur]) + .then(([prev, cur]) => item(prev, cur, ...args),) + .then(joint => Promise.all([ + joint && ref.whenWrote(joint), + ref.whenWrote(cur) + ])) + ; + } + + this.shift(1, ([first]) => ref.push(prev = first)) + .consume(consumer) + .then(() => ref.end()); + + return ref; + }, + + /** + * Keep a buffer of n-chunks for use with {@see DataStream..rewind} + * + * @chainable + * @memberof module:scramjet.DataStream# + * @param {number} [count=Infinity] Number of objects or -1 for all the stream + * + * @test test/methods/data-stream-keep.js + */ + keep(count = -1) { + if (count < 0) + count = Infinity; + + this.pipe(this.buffer = new ReReadable({ length: count, objectMode: true })); + + return this.tap(); + }, + + /** + * Rewinds the buffered chunks the specified length backwards. Requires a prior call to {@see DataStream..keep} + * + * @chainable + * @memberof module:scramjet.DataStream# + * @param {number} [count=Infinity] Number of objects or -1 for all the buffer + */ + rewind(count = -1) { + if (count < 0) + count = Infinity; + + if (this.buffer) { + return this.buffer.tail(count).pipe(this._selfInstance()); + } else { + throw new Error("Stream not buffered, cannot rewind."); + } + }, + + /** + * Returns a stream that stacks up incoming items always feeding out the newest items first. + * It returns the older items when read + * + * When the stack length exceeds the given `count` the given `drop` function is awaited + * and used for flow control. + * + * By default the drop function ignores and quietly disposes of items not read before overflow. + * + * @chainable + * @param {number} [count=1000] + * @param {function} [drop] + * @memberof module:scramjet.DataStream# + * + * @test test/methods/data-stream-stack.js + */ + stack(count = 1000, drop = () => 0) { + if (count < 0) + count = Infinity; + + return this.tap().use( + source => { + const stack = []; + const waiting = []; + let end = false; + const target = new this.constructor({referrer: this, promiseRead() { + if (end) { + if (stack.length) { + const out = stack.slice().reverse(); + stack.length = 0; + return out; + } else { + return []; + } + } + if (stack.length) { + return [stack.pop()]; + } + + return new Promise(res => { + waiting.push(res); + }); + }}); + + source + .each(item => { + if (waiting.length === 0) { + stack.push(item); + if (stack.length > count) { + return drop(stack.shift()); + } + } else { + waiting.shift()([item]); + } + }) + .catch( + e => target.raise(e) + ) + .whenEnd() + .then(() => { + end = true; + if (waiting.length) { + console.log(waiting[0]); + waiting.shift()([]); + } + }); + + return target; + } + ); + }, + + /** + * Distributes processing into multiple sub-processes or threads if you like. + * + * @todo Currently order is not kept. + * @todo Example test breaks travis-ci build + * + * @chainable + * @memberof module:scramjet.DataStream# + * @param {AffinityCallback|Function|number} [affinity] A Function that affixes the item to specific output stream which must exist in the object for each chunk, must return a string. A number may be passed to identify how many round-robin threads to start up. Defaults to Round Robin to twice the number of CPU threads. + * @param {Function|DataStreamOptions} [clusterFunc] stream transforms similar to {@see DataStream#use method} + * @param {DataStreamOptions} [options] Options + * + * @test test/methods/data-stream-distribute.js + */ + distribute(affinity, clusterFunc = null, { + plugins = [], + options = {} + } = {}) { + + if (!clusterFunc && affinity) { + clusterFunc = affinity; + affinity = os.cpus().length * 2; + } + + if (typeof affinity === "number") { + const roundRobinLength = affinity; + let z = 0; + options.threads = affinity; + affinity = () => z = ++z % roundRobinLength; + } + + if (!Array.isArray(clusterFunc)) + clusterFunc = [clusterFunc]; + + const streams = this + .separate(affinity, options.createOptions, this.constructor) + .cluster(clusterFunc, { + plugins, + threads: options.threads, + StreamClass: this.constructor + }); + + return streams.mux(); + }, + + /** + * Separates stream into a hash of streams. Does not create new streams! + * + * @chainable + * @meta.noreadme + * @memberof module:scramjet.DataStream# + * @param {object} streams the object hash of streams. Keys must be the outputs of the affinity function + * @param {AffinityCallback} affinity the Function that affixes the item to specific streams which must exist in the object for each chunk. + */ + separateInto(streams, affinity) { + this.consume( + async (chunk) => { + const streamId = await affinity(chunk); + const found = streams[streamId]; + + if (found) { + return found.whenWrote(chunk); + } + + throw new Error("Output for " + streamId + " not found in " + JSON.stringify(chunk)); + } + ); + return this; + }, + + /** + * @callback AffinityCallback + * @memberof module:scramjet~ + * @param {*} chunk + * @returns {Symbol|string} + */ + + /** + * Separates execution to multiple streams using the hashes returned by the passed Function. + * + * Calls the given Function for a hash, then makes sure all items with the same hash are processed within a single + * stream. Thanks to that streams can be distributed to multiple threads. + * + * @meta.noreadme + * @chainable + * @memberof module:scramjet.DataStream# + * @param {AffinityCallback} affinity the affinity function + * @param {DataStreamOptions} [createOptions] options to use to create the separated streams + * @param {function(new:DataStream)} [ClassType=this.constructor] options to use to create the separated streams + * @return {MultiStream} separated stream + * + * @test test/methods/data-stream-separate.js + */ + separate(affinity, createOptions = {}, ClassType = this.constructor) { + const ret = new MultiStream(); + const hashes = new Map(); + + ClassType = ClassType || this.constructor; + + const pushChunk = async (hash, chunk) => { + const _hash = hash.toString(); + let rightStream; + if (!hashes.has(_hash)) { + rightStream = new ClassType(createOptions); + rightStream._separateId = _hash; + hashes.set(_hash, rightStream); + ret.add(rightStream); + } else { + rightStream = hashes.get(_hash); + } + + return rightStream.whenWrote(chunk); + }; + + this.pipe( + new this.constructor({ + async promiseTransform(chunk) { + try { + const hash = await affinity(chunk); + if (Array.isArray(hash)) return Promise.all(hash.map(str => pushChunk(str, chunk))); + else return pushChunk(hash, chunk); + } catch (e) { + ret.emit("error", e); + } + }, + referrer: this + }) + .on("end", () => { + ret.streams.forEach(stream => stream.end()); + }) + .resume() + ); + + return ret; + }, + + /** + * @memberof module:scramjet~ + * @typedef {Function} DelegateCallback + */ + + /** + * Delegates work to a specified worker. + * + * @meta.noreadme + * @chainable + * @memberof module:scramjet.DataStream# + * @param {DelegateCallback} delegateFunc A function to be run in the sub-thread. + * @param {StreamWorker} worker + * @param {Array} [plugins=[]] + */ + delegate(delegateFunc, worker, plugins = []) { + const ret = this._selfInstance({referrer: this}); + return worker.delegate(this, delegateFunc, plugins).pipe(ret); + }, + + /** + * Limit the rate of the stream to a given number of chunks per second or given timeframe. + * + * @meta.noreadme + * @chainable + * @memberof module:scramjet.DataStream# + * @param {number} cps Chunks per timeframe, the default timeframe is 1000 ms. + * @param {RateOptions} [options={}] Options for the limiter controlling the timeframe and time source. Both must work on same units. + */ + rate(cps, {windowSize = 1000, getTime = Date.now, setTimeout = global.setTimeout} = {}) { + const refs = []; + + const defer = (time) => new Promise(res => setTimeout(res, time)); + + return this.do( + () => { + const time = getTime(); + refs.push(time); + + if (refs.length <= cps) return; // DRY fail on purpose here - we don't need to slice the lemon twice. + + refs.splice(0, refs.find(x => x < time - windowSize)); + if (refs.length <= cps) return; + + return defer(time - refs.shift() + windowSize); + } + ); + }, + + /** + * @typedef {object} RateOptions + * @memberof module:scramjet~ + * @param {number} [timeFrame=1000] The size of the window to look for streams. + * @param {Function} [getTime=Date.now] Time source - anything that returns time. + * @param {Function} [setTimeout=setTimeout] Timing function that works identically to setTimeout. + */ + + /** + * Aggregates chunks in arrays given number of number of items long. + * + * This can be used for micro-batch processing. + * + * @chainable + * @memberof module:scramjet.DataStream# + * @param {number} count How many items to aggregate + * + * @test test/methods/data-stream-batch.js + */ + batch(count) { + let arr = []; + + const ret = this.tap().pipe(new this.constructor({ + promiseTransform(chunk) { + arr.push(chunk); + if (arr.length >= count) { + const push = arr; + arr = []; + return push; + } + return Promise.reject(DataStream.filter); + }, + promiseFlush() { + if (arr.length > 0) { + return [arr]; + } else + return []; + }, + referrer: this + })); + + return ret; + }, + + /** + * Aggregates chunks to arrays not delaying output by more than the given number of ms. + * + * @meta.noreadme + * @chainable + * @memberof module:scramjet.DataStream# + * @param {number} ms Maximum amount of milliseconds + * @param {number} [count] Maximum number of items in batch (otherwise no limit) + * + * @test test/methods/data-stream-timebatch.js + */ + timeBatch(ms, count = Infinity) { + let arr = []; + + const setTimeout = this.setTimeout; + const clearTimeout = this.clearTimeout; + + let ret = this._selfInstance({referrer: this}); + + let pushTimeout = null; + + const push = () => { + if (pushTimeout) { + clearTimeout(pushTimeout); + pushTimeout = null; + } + const last = ret.whenWrote(arr); + arr = []; + return last; + }; + + this.consume(async (chunk) => { + arr.push(chunk); + if (arr.length >= count) { + await push(); + } else if (!pushTimeout) { + pushTimeout = setTimeout(push, ms); + } + }).then(async () => { + if (arr.length) { + clearTimeout(pushTimeout); + await ret.whenWrote(arr); + } + ret.end(); + }); + + return ret; + }, + + /** + * Performs the Nagle's algorithm on the data. In essence it waits until we receive some more data and releases them + * in bulk. + * + * @memberof module:scramjet.DataStream# + * @todo needs more work, for now it's simply waiting some time, not checking the queues. + * @param {number} [size=32] maximum number of items to wait for + * @param {number} [ms=10] milliseconds to wait for more data + * @chainable + * @meta.noreadme + */ + nagle(size = 32, ms = 10) { + return this.timeBatch(size, ms) + .flatten(); + }, + + /** + * Returns a WindowStream of the specified length + * + * @memberof module:scramjet.DataStream# + * @chainable + * @param {number} length + * @returns {WindowStream} a stream of array's + * @meta.noreadme + */ + window(length) { + if (!(+length > 0)) + throw new Error("Length argument must be a positive integer!"); + + const arr = []; + return this.into( + (out, chunk) => { + arr.push(chunk); + if (arr.length > length) + arr.shift(); + + return out.whenWrote(arr.slice()); + }, + new scramjet.WindowStream() + ); + }, + + /** + * Transforms the stream to a streamed JSON array. + * + * @chainable + * @memberof module:scramjet.DataStream# + * @param {Iterable} [enclosure='[]'] Any iterable object of two items (beginning and end) + * @return {StringStream} + * @meta.noreadme + * + * @test test/methods/data-stream-tojsonarray.js + */ + toJSONArray(enclosure = ["[\n", "\n]"], separator = ",\n", stringify = JSON.stringify) { + const ref = new StringStream({referrer: this}); + this + .shift(1, ([first]) => { + ref.push(enclosure[0]); + if (first) return ref.whenWrote(stringify(first)); + }) + .each( + (chunk) => Promise.all([ + ref.whenWrote(separator), + ref.whenWrote(stringify(chunk)) + ]) + ) + .run() + .then( + () => ref.end(enclosure[1]) + ); + + return ref; + }, + + /** + * Transforms the stream to a streamed JSON object. + * + * @meta.noreadme + * @chainable + * @memberof module:scramjet.DataStream# + * @param {MapCallback} [entryCallback] async function returning an entry (array of [key, value]) + * @param {Iterable} [enclosure='{}'] Any iterable object of two items (beginning and end) + * @return {StringStream} + * @meta.noreadme + * + * @test test/methods/data-stream-tojsonobject.js + */ + toJSONObject(entryCallback, enclosure = ["{\n","\n}"], separator = ",\n") { + let ref = this; + + return ref.map((item) => [entryCallback(item), item]) + .toJSONArray(enclosure, separator, ([key, value]) => JSON.stringify(key.toString()) + ":" + JSON.stringify(value)); + }, + + + /** + * Returns a StringStream containing JSON per item with optional end line + * + * @meta.noreadme + * @chainable + * @memberof module:scramjet.DataStream# + * @param {Boolean|string} [endline=os.EOL] whether to add endlines (boolean or string as delimiter) + * @return {StringStream} output stream + * + * @test test/methods/data-stream-jsonstringify.js + */ + JSONStringify(eol = os.EOL) { + if (!eol) + eol = ""; + + return this.stringify((item) => JSON.stringify(item) + eol); + }, + + /** + * Stringifies CSV to DataString using 'papaparse' module. + * + * @chainable + * @memberof module:scramjet.DataStream# + * @param {object} [options={}] options for the papaparse.unparse module. + * @return {StringStream} stream of parsed items + * + * @test test/methods/data-stream-csv.js + */ + CSVStringify(options = {}) { + const Papa = __nccwpck_require__(877); + let header = null; + let start = 1; + options = Object.assign({ + header: true, + newline: os.EOL + }, options); + + const outOptions = Object.assign({}, options, { + header: false + }); + + return this + .timeBatch(16, 64) + .map((arr) => { + const out = []; + if (!header) { + header = Object.keys(arr[0]); + if (options.header) out.push(header); + } + for (const item of arr) + out.push(header.map(key => item[key])); + + const x = Papa.unparse(out, outOptions) + options.newline; + if (start) { + start = 0; + return x; + } + return x; + }) + .pipe(new StringStream()); + }, + + /** + * @typedef {object} ExecDataOptions + * @memberof module:scramjet~ + * @property {UseCallback} [parse] scramjet module to transform the stream to string or buffer stream + * @property {UseCallback} [stringify] scramjet module to transform from string or buffer stream to wanted version + * @extends StringStream.ExecOptions + */ + + /** + * Executes a given sub-process with arguments and pipes the current stream into it while returning the output as another DataStream. + * + * Pipes the current stream into the sub-processes stdin. + * The data is serialized and deserialized as JSON lines by default. You + * can provide your own alternative methods in the ExecOptions object. + * + * Note: if you're piping both stderr and stdout (options.stream=3) keep in mind that chunks may get mixed up! + * + * @param {string} command command to execute + * @memberof module:scramjet.DataStream# + * @param {ExecDataOptions|any} [options={}] options to be passed to `spawn` and defining serialization. + * @param {string[]} ...args additional args will be passed to function + * + * @test test/methods/data-stream-exec.js + */ + exec(command, options = {}, ...args) { + const resolvedCmd = path.resolve(getCalleeDirname(1), command); + const stringify = options.stringify || (stream => stream.JSONStringify()); + const parse = options.parse || (stream => stream.JSONParse()); + + return this + .use(stringify) + .exec(resolvedCmd, options, ...args) + .use(parse); + }, + + /** + * Injects a ```debugger``` statement when called. + * + * @meta.noreadme + * @chainable + * @memberof module:scramjet.DataStream# + * @param {Function} func if passed, the function will be called on self to add an option to inspect the stream in place, while not breaking the transform chain + * @return {DataStream} self + * + * @test test/methods/data-stream-debug.js + */ + debug(func) { + debugger; // eslint-disable-line + this.use(func); + return this; + }, + +}; + +module.exports.pop = module.exports.shift; +module.exports.group = module.exports.separate; + + +/***/ }), + +/***/ 8750: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +module.exports = __nccwpck_require__(203); + +module.exports.plugin({ + /** + * A Stream Worker class + * + * @inject StreamWorker + * @memberof module:scramjet + * @type {StreamWorker} + */ + StreamWorker: __nccwpck_require__(7010) +}); + +module.exports.plugin({ + DataStream: __nccwpck_require__(9738), + BufferStream: __nccwpck_require__(3946), + StringStream: __nccwpck_require__(6687), + MultiStream: __nccwpck_require__(9577), + NumberStream: __nccwpck_require__(223) +}).plugin({ + WindowStream: __nccwpck_require__(2374) +}); + + +/***/ }), + +/***/ 9577: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +const scramjet = __nccwpck_require__(203); +const os = __nccwpck_require__(2037); + +module.exports = { + /** + * Re-routes streams to a new MultiStream of specified size + * + * @meta.noreadme + * @memberof module:scramjet.MultiStream# + * @todo NYT: not yet tested + * @todo NYD: not yet documented + * @param {Function} [policy=Affinity.RoundRobin] [description] + * @param {number} [count=os.cpus().length] [description] + * @return {MultiStream} [description] + */ + route(policy, count = os.cpus().length) { + const affine = policy(null, count); + return this.mux().separate( + async (item) => await affine(item) + ); + }, + + /** + * Map stream synchronously + * + * @chainable + * @memberof module:scramjet.MultiStream# + * @param {Function} transform mapping function ran on every stream (SYNCHRONOUS!) + */ + smap(transform) { + const out = new this.constructor(this.streams.map(transform)); + this.each( + (stream) => out.add(transform(stream)), + (stream) => out.remove(transform(stream)) + ); + return out; + }, + + /** + * Distribute options + * + * @typedef {object} DistributeOptions + * @memberof module:scramjet~ + * @prop {Array} [plugins=[]] a list of scramjet plugins to load (if omitted, will use just the ones in scramjet itself) + * @prop {string} [StreamClass=DataStream] the class to deserialize the stream to. + * @prop {number} [threads=os.cpus().length * 2] maximum threads to use - defaults to number of processor threads in os, but it may be sensible to go over this value if you'd intend to run synchronous code. + * @prop {DataStreamOptions} [createOptions={}] maximum threads to use - defaults to number of processor threads in os, but it may be sensible to go over this value if you'd intend to run synchronous code. + * @prop {StreamWorker} [StreamWorker=scramjet.StreamWorker] worker implementation. + */ + + /** + * Distributes processing to multiple forked subprocesses. + * + * @chainable + * @memberof module:scramjet.MultiStream# + * @param {Function|string} clusterFunc a cluster callback with all operations working similarly to DataStream::use + * @param {DistributeOptions} [options={}] + */ + cluster(clusterFunc, {plugins = [], threads = os.cpus().length * 2, StreamClass = scramjet.DataStream, createOptions = {}, StreamWorker = scramjet.StreamWorker} = {}) { + const out = new this.constructor(); + + StreamWorker.fork(threads); + + this.each( + (stream) => StreamWorker + .fork(threads) + .then( + ([worker]) => out.add(worker.delegate(stream, clusterFunc, plugins).pipe(new StreamClass(createOptions))) + ) + ); + + return out; + }, + +}; + + +/***/ }), + +/***/ 223: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +const {DataStream} = __nccwpck_require__(203); + +/** + * Simple scramjet stream that by default contains numbers or other containing with `valueOf` method. The streams + * provides simple methods like `sum`, `average`. It derives from DataStream so it's still fully supporting all `map`, + * `reduce` etc. + * + * @memberof module:scramjet. + * @extends DataStream + */ +class NumberStream extends DataStream { + + /** + * @callback ValueOfCallback + * @memberof module:scramjet~ + * @param {*} chunk stream object + * @returns {Promise|number} value of the object + */ + + /** + * NumberStream options + * + * @typedef {object} NumberStreamOptions + * @memberof module:scramjet~ + * @prop {ValueOfCallback} [valueOf=x => +x] value of the data item function. + * @extends DataStreamOptions + */ + + /** + * Creates an instance of NumberStream. + * @param {NumberStreamOptions} options + * @memberof module:scramjet + */ + constructor(options, ...args) { + super(options, ...args); + } + + get _valueOf() { + return this._options.valueOf || ((x) => +x); + } + + /** + * Calculates the sum of all items in the stream. + * + * @return {Promise|any} + */ + async sum() { + const _valueOf = this._valueOf; + return this.reduce(async (a, x) => a + await _valueOf(x), 0); + } + + /** + * Calculates the sum of all items in the stream. + * + * @return {Promise|any} + */ + async avg() { + let cnt = 0; + const _valueOf = this._valueOf; + return this.reduce(async (a, x) => (cnt * a + await _valueOf(x)) / ++cnt, 0); + } + +} + +module.exports = NumberStream; + + +/***/ }), + +/***/ 7010: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +const {DataStream, StringStream} = __nccwpck_require__(203); +const ref = {}; +const {fork} = __nccwpck_require__(2081); +const os = __nccwpck_require__(2037); + +const net = __nccwpck_require__(1808); + +let workerSeq = 0; +const workers = []; + +/** + * StreamWorker class - intended for internal use + * + * This class provides control over the subprocesses, including: + * - spawning + * - communicating + * - delivering streams + * + * @internal + * @memberof module:scramjet + */ +class StreamWorker { + + /** + * Private constructor + */ + constructor(def) { + if (def !== ref) { + throw new Error("Private constructor"); + } + + this._refcount = 0; + + this.timeout = 1e3; + this.child = null; + this.resolving = null; + + this.ip = "localhost"; + this.port = 0; + } + + /** + * Spawns the worker if necessary and provides the port information to it. + * + * @async + * @return {StreamWorker} + */ + async spawn() { + return this.resolving || (this.resolving = new Promise((res, rej) => { + this._child = fork(__dirname + "/stream-child", [this.ip, this.timeout]); + + let resolved = false; + let rejected = false; + + this._child.once("message", ({port}) => (this.port = +port, resolved = true, res(this))); + this._child.once("error", (e) => (rejected = true, rej(e))); + this._child.once("exit", (code) => { + if (!code) // no error, child exited, clear the promise so that it respawns when needed + this.resolving = null; + else + this.resolving = Promise.reject(new Error("Child exited with non-zero status code: " + code)); + }); + + setTimeout(() => resolved || rejected || (rejected = true) && rej(new Error("StreamWorker child timeout!")), this.timeout); + })); + } + + /** + * Delegates a stream to the child using tcp socket. + * + * The stream gets serialized using JSON and passed on to the sub-process. + * The sub-process then performs transforms on the stream and pushes them back to the main process. + * The stream gets deserialized and outputted to the returned DataStream. + * + * @param {DataStream} input stream to be delegated + * @param {TeeCallback[]|Array} delegateFunc Array of transforms or arrays describing ['module', 'method'] + * @param {any[]} [plugins=[]] List of plugins to load in the child + * @return {DataStream} stream after transforms and back to the main process. + */ + delegate(input, delegateFunc, plugins = []) { + + const sock = new net.Socket().setNoDelay(true); + + const _in = new DataStream(); + + const _out = _in + .JSONStringify() + .pipe(sock.connect(this.port, this.ip)) + .pipe(new StringStream) + .JSONParse() + .filter( + ({error}) => { + if (error) { + const err = new Error(error.message); + err.stack = "[child]" + error.stack; + } + return true; + } + ) + ; + + _in.unshift({ + type: 0, // 0 - start, 1 - end + plugins, + streamClass: input.constructor.name, + transforms: delegateFunc.map( + func => typeof func === "function" ? func.toString() : (Array.isArray(func) ? func : [func]) + ) + }); + + input.pipe(_in); + + return _out; + } + + /** + * Spawns (Preforks) a given number of subprocesses and returns the worker asynchronously. + * + * @async + * @param {number} [count=os.cpus().length] Number of processes to spawn. If other subprocesses are active only the missing ones will be spawned. + * @return {Array} list of StreamWorkers + */ + static async fork(count = os.cpus().length) { + + for (let i = workers.length; i < count; i++) + workers.push(new StreamWorker(ref)); + + return Promise.all(new Array(count).fill(1).map(() => this._getWorker())); + } + + /** + * Picks next worker (not necessarily free one!) + * + * @async + * @return {StreamWorker} + */ + static async _getWorker() { + // TODO: Use free / not fully utilized workers first. + + return workers[workerSeq = ++workerSeq % workers.length].spawn(); + } + +} + +module.exports = StreamWorker; + + +/***/ }), + +/***/ 6687: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +const {DataStream} = __nccwpck_require__(203); +const {spawn} = __nccwpck_require__(2081); +const path = __nccwpck_require__(1017); + +const platform = (__nccwpck_require__(2037).platform)(); + +/** @ignore */ +const getCalleeDirname = (__nccwpck_require__(8603).getCalleeDirname); + +module.exports = { + + /** + * Splits the string stream by the specified regexp or string + * + * @chainable + * @memberof module:scramjet.StringStream# + * @param {string|RegExp} [eol=/\r?\n/] End of line string or regex + * + * @test test/methods/string-stream-split.js + */ + lines(eol = /\r?\n/) { + return this.split(eol); + }, + + /** + * Parses each entry as JSON. + * Ignores empty lines + * + * @chainable + * @memberof module:scramjet.StringStream# + * @param {Boolean} [perLine=true] instructs to split per line + * @return {DataStream} stream of parsed items + */ + JSONParse(perLine = true) { + let str = this; + if (perLine) { + str = str.lines(); + } + + return str.filter(a => a !== "").parse(JSON.parse); + }, + + /** + * Parses CSV to DataString using 'papaparse' module. + * + * @chainable + * @memberof module:scramjet.StringStream# + * @param {object} [options={}] options for the papaparse.parse method. + * @return {DataStream} stream of parsed items + * @test test/methods/data-stream-separate.js + */ + CSVParse(options = {}) { + const out = new DataStream(); + (__nccwpck_require__(877).parse)(this, Object.assign(options, { + chunk: async ({data, errors}, parser) => { + if (errors.length) { + return out.raise(Object.assign(new Error(), errors[0])); + } + + if (!out.write(data)) { + parser.pause(); + await out.whenDrained(); + parser.resume(); + } + }, + complete: () => { + out.end(); + }, + error: (e) => { + out.emit("error", e); + } + })); + + return out.flatten(); + }, + + /** + * Appends given argument to all the items. + * + * @chainable + * @memberof module:scramjet.StringStream# + * @param {ThenFunction|string} param the argument to append. If function passed then it will be called and resolved and the resolution will be appended. + * + * @test test/methods/string-stream-append.js + */ + append(param) { + return typeof param === "function" ? this.map(item => Promise.resolve(item).then(param).then((result) => item + result)) : this.map(item => item + param); + }, + + /** + * Prepends given argument to all the items. + * + * @chainable + * @memberof module:scramjet.StringStream# + * @param {ThenFunction|string} param the argument to prepend. If function passed then it will be called and resolved + * and the resolution will be prepended. + * + * @test test/methods/string-stream-prepend.js + */ + prepend(param) { + return typeof param === "function" ? this.map(item => Promise.resolve(item).then(param).then((result) => result + item)) : this.map(item => param + item); + }, + + /** + * @typedef {object} ExecOptions + * @memberof module:scramjet~ + * @property {number} [stream=1] (bitwise) the output stdio number to push out (defaults to stdout = 1) + * @property {string[]} [interpreter=[]] defaults to nothing, except on windows where "cmd.exe /c" will be spawned by default + * @extends child_process.SpawnOptions + */ + + /** + * Executes a given sub-process with arguments and pipes the current stream into it while returning the output as another DataStream. + * + * Pipes the current stream into the sub-processes stdin. + * The data is serialized and deserialized as JSON lines by default. You + * can provide your own alternative methods in the ExecOptions object. + * + * Note: if you're piping both stderr and stdout (options.stream=3) keep in mind that chunks may get mixed up! + * + * @param {string} command command to execute + * @memberof module:scramjet.StringStream# + * @param {ExecOptions|any} [options={}] options to be passed to `spawn` and defining serialization. + * @param {string[]} ...args additional arguments (will overwrite to SpawnOptions args even if not given) + * + * @test test/methods/string-stream-exec.js + */ + exec(command, options, ...args) { + const shell = process.env.SHELL ? [process.env.SHELL] : platform === "win32" ? ["cmd.exe","/c"] : []; + const { stream = 1, interpreter = shell } = (options || {}); + + const out = this.tap()._selfInstance({referrer: this}); + + const spawnOptions = Object.assign({}, options); + spawnOptions.stdio = ["pipe", "pipe", "pipe"]; + + const resolvedCommand = path.resolve(getCalleeDirname(1), command); + + const actualArgs = interpreter && interpreter.length ? [...interpreter.slice(1), resolvedCommand, ...args] : args; + const actualCmd = interpreter && interpreter.length ? interpreter[0] : resolvedCommand; + + let end = 1; + const ended = () => end-- || out.end(); + + const proc = spawn(actualCmd, actualArgs, spawnOptions); + this.catch(e => proc.kill(e.signal)); + proc.on("error", e => { + ended(); + return out.raise(e); + }); + proc.on("exit", async exitCode => { + if (exitCode > 0) { + const error = Object.assign(new Error(`Non-zero exitcode (${exitCode}) returned from command "${resolvedCommand}"`), {exitCode}); + await out.raise(error); + } + + ended(); + }); + + this.pipe(proc.stdin); + proc.stdin.on("error", async e => { + if (!(e.code === "EPIPE")) { + await out.raise(e); + } + // ignore EPIPE after end + }); + if (stream & 1) proc.stdout.on("end", () => ended()).pipe(out, {end: false}); + if (stream & 2) proc.stderr.on("end", () => ended()).pipe(out, {end: false}); + + return out; + + } + +}; + + +/***/ }), + +/***/ 2374: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +const {NumberStream} = __nccwpck_require__(203); + +/** + * A stream for moving window calculation with some simple methods. + * + * In essence it's a stream of Array's containing a list of items - a window. + * It's best used when created by the `DataStream..window`` method. + * + * @memberof module:scramjet. + * @extends NumberStream + */ +class WindowStream extends NumberStream { + + /** + * Calculates moving sum of items, the output NumberStream will contain the moving sum. + * + * @chainable + * @param {ValueOfCallback} [valueOf] value of method for array items + * @return {NumberStream} + */ + sum(valueOf = (x) => +x) { + return this.map( + arr => arr.reduce((a, x) => a + valueOf(x)), + NumberStream + ); + } + + /** + * Calculates the moving average of the window and returns the NumberStream + * + * @chainable + * @param {ValueOfCallback} [valueOf] value of method for array items + * @return {NumberStream} + */ + avg(valueOf = (x) => +x) { + let cnt = 0; + return this.map( + arr => arr.reduce((a, x) => (cnt * a + valueOf(x)) / ++cnt, 0), + NumberStream + ); + } +} + +module.exports = WindowStream; + + +/***/ }), + +/***/ 4294: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +module.exports = __nccwpck_require__(4219); + + +/***/ }), + +/***/ 4219: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +var net = __nccwpck_require__(1808); +var tls = __nccwpck_require__(4404); +var http = __nccwpck_require__(3685); +var https = __nccwpck_require__(5687); +var events = __nccwpck_require__(2361); +var assert = __nccwpck_require__(9491); +var util = __nccwpck_require__(3837); + + +exports.httpOverHttp = httpOverHttp; +exports.httpsOverHttp = httpsOverHttp; +exports.httpOverHttps = httpOverHttps; +exports.httpsOverHttps = httpsOverHttps; + + +function httpOverHttp(options) { + var agent = new TunnelingAgent(options); + agent.request = http.request; + return agent; +} + +function httpsOverHttp(options) { + var agent = new TunnelingAgent(options); + agent.request = http.request; + agent.createSocket = createSecureSocket; + agent.defaultPort = 443; + return agent; +} + +function httpOverHttps(options) { + var agent = new TunnelingAgent(options); + agent.request = https.request; + return agent; +} + +function httpsOverHttps(options) { + var agent = new TunnelingAgent(options); + agent.request = https.request; + agent.createSocket = createSecureSocket; + agent.defaultPort = 443; + return agent; +} + + +function TunnelingAgent(options) { + var self = this; + self.options = options || {}; + self.proxyOptions = self.options.proxy || {}; + self.maxSockets = self.options.maxSockets || http.Agent.defaultMaxSockets; + self.requests = []; + self.sockets = []; + + self.on('free', function onFree(socket, host, port, localAddress) { + var options = toOptions(host, port, localAddress); + for (var i = 0, len = self.requests.length; i < len; ++i) { + var pending = self.requests[i]; + if (pending.host === options.host && pending.port === options.port) { + // Detect the request to connect same origin server, + // reuse the connection. + self.requests.splice(i, 1); + pending.request.onSocket(socket); + return; + } + } + socket.destroy(); + self.removeSocket(socket); + }); +} +util.inherits(TunnelingAgent, events.EventEmitter); + +TunnelingAgent.prototype.addRequest = function addRequest(req, host, port, localAddress) { + var self = this; + var options = mergeOptions({request: req}, self.options, toOptions(host, port, localAddress)); + + if (self.sockets.length >= this.maxSockets) { + // We are over limit so we'll add it to the queue. + self.requests.push(options); + return; + } + + // If we are under maxSockets create a new one. + self.createSocket(options, function(socket) { + socket.on('free', onFree); + socket.on('close', onCloseOrRemove); + socket.on('agentRemove', onCloseOrRemove); + req.onSocket(socket); + + function onFree() { + self.emit('free', socket, options); + } + + function onCloseOrRemove(err) { + self.removeSocket(socket); + socket.removeListener('free', onFree); + socket.removeListener('close', onCloseOrRemove); + socket.removeListener('agentRemove', onCloseOrRemove); + } + }); +}; + +TunnelingAgent.prototype.createSocket = function createSocket(options, cb) { + var self = this; + var placeholder = {}; + self.sockets.push(placeholder); + + var connectOptions = mergeOptions({}, self.proxyOptions, { + method: 'CONNECT', + path: options.host + ':' + options.port, + agent: false, + headers: { + host: options.host + ':' + options.port + } + }); + if (options.localAddress) { + connectOptions.localAddress = options.localAddress; + } + if (connectOptions.proxyAuth) { + connectOptions.headers = connectOptions.headers || {}; + connectOptions.headers['Proxy-Authorization'] = 'Basic ' + + new Buffer(connectOptions.proxyAuth).toString('base64'); + } + + debug('making CONNECT request'); + var connectReq = self.request(connectOptions); + connectReq.useChunkedEncodingByDefault = false; // for v0.6 + connectReq.once('response', onResponse); // for v0.6 + connectReq.once('upgrade', onUpgrade); // for v0.6 + connectReq.once('connect', onConnect); // for v0.7 or later + connectReq.once('error', onError); + connectReq.end(); + + function onResponse(res) { + // Very hacky. This is necessary to avoid http-parser leaks. + res.upgrade = true; + } + + function onUpgrade(res, socket, head) { + // Hacky. + process.nextTick(function() { + onConnect(res, socket, head); + }); + } + + function onConnect(res, socket, head) { + connectReq.removeAllListeners(); + socket.removeAllListeners(); + + if (res.statusCode !== 200) { + debug('tunneling socket could not be established, statusCode=%d', + res.statusCode); + socket.destroy(); + var error = new Error('tunneling socket could not be established, ' + + 'statusCode=' + res.statusCode); + error.code = 'ECONNRESET'; + options.request.emit('error', error); + self.removeSocket(placeholder); + return; + } + if (head.length > 0) { + debug('got illegal response body from proxy'); + socket.destroy(); + var error = new Error('got illegal response body from proxy'); + error.code = 'ECONNRESET'; + options.request.emit('error', error); + self.removeSocket(placeholder); + return; + } + debug('tunneling connection has established'); + self.sockets[self.sockets.indexOf(placeholder)] = socket; + return cb(socket); + } + + function onError(cause) { + connectReq.removeAllListeners(); + + debug('tunneling socket could not be established, cause=%s\n', + cause.message, cause.stack); + var error = new Error('tunneling socket could not be established, ' + + 'cause=' + cause.message); + error.code = 'ECONNRESET'; + options.request.emit('error', error); + self.removeSocket(placeholder); + } +}; + +TunnelingAgent.prototype.removeSocket = function removeSocket(socket) { + var pos = this.sockets.indexOf(socket) + if (pos === -1) { + return; + } + this.sockets.splice(pos, 1); + + var pending = this.requests.shift(); + if (pending) { + // If we have pending requests and a socket gets closed a new one + // needs to be created to take over in the pool for the one that closed. + this.createSocket(pending, function(socket) { + pending.request.onSocket(socket); + }); + } +}; + +function createSecureSocket(options, cb) { + var self = this; + TunnelingAgent.prototype.createSocket.call(self, options, function(socket) { + var hostHeader = options.request.getHeader('host'); + var tlsOptions = mergeOptions({}, self.options, { + socket: socket, + servername: hostHeader ? hostHeader.replace(/:.*$/, '') : options.host + }); + + // 0 is dummy port for v0.6 + var secureSocket = tls.connect(0, tlsOptions); + self.sockets[self.sockets.indexOf(socket)] = secureSocket; + cb(secureSocket); + }); +} + + +function toOptions(host, port, localAddress) { + if (typeof host === 'string') { // since v0.10 + return { + host: host, + port: port, + localAddress: localAddress + }; + } + return host; // for v0.11 or later +} + +function mergeOptions(target) { + for (var i = 1, len = arguments.length; i < len; ++i) { + var overrides = arguments[i]; + if (typeof overrides === 'object') { + var keys = Object.keys(overrides); + for (var j = 0, keyLen = keys.length; j < keyLen; ++j) { + var k = keys[j]; + if (overrides[k] !== undefined) { + target[k] = overrides[k]; + } + } + } + } + return target; +} + + +var debug; +if (process.env.NODE_DEBUG && /\btunnel\b/.test(process.env.NODE_DEBUG)) { + debug = function() { + var args = Array.prototype.slice.call(arguments); + if (typeof args[0] === 'string') { + args[0] = 'TUNNEL: ' + args[0]; + } else { + args.unshift('TUNNEL:'); + } + console.error.apply(console, args); + } +} else { + debug = function() {}; +} +exports.debug = debug; // for test + + +/***/ }), + +/***/ 1773: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const Client = __nccwpck_require__(3598) +const Dispatcher = __nccwpck_require__(412) +const errors = __nccwpck_require__(8045) +const Pool = __nccwpck_require__(4634) +const BalancedPool = __nccwpck_require__(7931) +const Agent = __nccwpck_require__(7890) +const util = __nccwpck_require__(3983) +const { InvalidArgumentError } = errors +const api = __nccwpck_require__(4059) +const buildConnector = __nccwpck_require__(2067) +const MockClient = __nccwpck_require__(8687) +const MockAgent = __nccwpck_require__(6771) +const MockPool = __nccwpck_require__(6193) +const mockErrors = __nccwpck_require__(888) +const ProxyAgent = __nccwpck_require__(7858) +const { getGlobalDispatcher, setGlobalDispatcher } = __nccwpck_require__(1892) +const DecoratorHandler = __nccwpck_require__(6930) +const RedirectHandler = __nccwpck_require__(2860) +const createRedirectInterceptor = __nccwpck_require__(8861) + +let hasCrypto +try { + __nccwpck_require__(6113) + hasCrypto = true +} catch { + hasCrypto = false +} + +Object.assign(Dispatcher.prototype, api) + +module.exports.Dispatcher = Dispatcher +module.exports.Client = Client +module.exports.Pool = Pool +module.exports.BalancedPool = BalancedPool +module.exports.Agent = Agent +module.exports.ProxyAgent = ProxyAgent + +module.exports.DecoratorHandler = DecoratorHandler +module.exports.RedirectHandler = RedirectHandler +module.exports.createRedirectInterceptor = createRedirectInterceptor + +module.exports.buildConnector = buildConnector +module.exports.errors = errors + +function makeDispatcher (fn) { + return (url, opts, handler) => { + if (typeof opts === 'function') { + handler = opts + opts = null + } + + if (!url || (typeof url !== 'string' && typeof url !== 'object' && !(url instanceof URL))) { + throw new InvalidArgumentError('invalid url') + } + + if (opts != null && typeof opts !== 'object') { + throw new InvalidArgumentError('invalid opts') + } + + if (opts && opts.path != null) { + if (typeof opts.path !== 'string') { + throw new InvalidArgumentError('invalid opts.path') + } + + let path = opts.path + if (!opts.path.startsWith('/')) { + path = `/${path}` + } + + url = new URL(util.parseOrigin(url).origin + path) + } else { + if (!opts) { + opts = typeof url === 'object' ? url : {} + } + + url = util.parseURL(url) + } + + const { agent, dispatcher = getGlobalDispatcher() } = opts + + if (agent) { + throw new InvalidArgumentError('unsupported opts.agent. Did you mean opts.client?') + } + + return fn.call(dispatcher, { + ...opts, + origin: url.origin, + path: url.search ? `${url.pathname}${url.search}` : url.pathname, + method: opts.method || (opts.body ? 'PUT' : 'GET') + }, handler) + } +} + +module.exports.setGlobalDispatcher = setGlobalDispatcher +module.exports.getGlobalDispatcher = getGlobalDispatcher + +if (util.nodeMajor > 16 || (util.nodeMajor === 16 && util.nodeMinor >= 8)) { + let fetchImpl = null + module.exports.fetch = async function fetch (resource) { + if (!fetchImpl) { + fetchImpl = (__nccwpck_require__(4881).fetch) + } + + try { + return await fetchImpl(...arguments) + } catch (err) { + if (typeof err === 'object') { + Error.captureStackTrace(err, this) + } + + throw err + } + } + module.exports.Headers = __nccwpck_require__(554).Headers + module.exports.Response = __nccwpck_require__(7823).Response + module.exports.Request = __nccwpck_require__(8359).Request + module.exports.FormData = __nccwpck_require__(2015).FormData + module.exports.File = __nccwpck_require__(8511).File + module.exports.FileReader = __nccwpck_require__(1446).FileReader + + const { setGlobalOrigin, getGlobalOrigin } = __nccwpck_require__(1246) + + module.exports.setGlobalOrigin = setGlobalOrigin + module.exports.getGlobalOrigin = getGlobalOrigin + + const { CacheStorage } = __nccwpck_require__(7907) + const { kConstruct } = __nccwpck_require__(9174) + + // Cache & CacheStorage are tightly coupled with fetch. Even if it may run + // in an older version of Node, it doesn't have any use without fetch. + module.exports.caches = new CacheStorage(kConstruct) +} + +if (util.nodeMajor >= 16) { + const { deleteCookie, getCookies, getSetCookies, setCookie } = __nccwpck_require__(1724) + + module.exports.deleteCookie = deleteCookie + module.exports.getCookies = getCookies + module.exports.getSetCookies = getSetCookies + module.exports.setCookie = setCookie + + const { parseMIMEType, serializeAMimeType } = __nccwpck_require__(685) + + module.exports.parseMIMEType = parseMIMEType + module.exports.serializeAMimeType = serializeAMimeType +} + +if (util.nodeMajor >= 18 && hasCrypto) { + const { WebSocket } = __nccwpck_require__(4284) + + module.exports.WebSocket = WebSocket +} + +module.exports.request = makeDispatcher(api.request) +module.exports.stream = makeDispatcher(api.stream) +module.exports.pipeline = makeDispatcher(api.pipeline) +module.exports.connect = makeDispatcher(api.connect) +module.exports.upgrade = makeDispatcher(api.upgrade) + +module.exports.MockClient = MockClient +module.exports.MockPool = MockPool +module.exports.MockAgent = MockAgent +module.exports.mockErrors = mockErrors + + +/***/ }), + +/***/ 7890: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { InvalidArgumentError } = __nccwpck_require__(8045) +const { kClients, kRunning, kClose, kDestroy, kDispatch, kInterceptors } = __nccwpck_require__(2785) +const DispatcherBase = __nccwpck_require__(4839) +const Pool = __nccwpck_require__(4634) +const Client = __nccwpck_require__(3598) +const util = __nccwpck_require__(3983) +const createRedirectInterceptor = __nccwpck_require__(8861) +const { WeakRef, FinalizationRegistry } = __nccwpck_require__(6436)() + +const kOnConnect = Symbol('onConnect') +const kOnDisconnect = Symbol('onDisconnect') +const kOnConnectionError = Symbol('onConnectionError') +const kMaxRedirections = Symbol('maxRedirections') +const kOnDrain = Symbol('onDrain') +const kFactory = Symbol('factory') +const kFinalizer = Symbol('finalizer') +const kOptions = Symbol('options') + +function defaultFactory (origin, opts) { + return opts && opts.connections === 1 + ? new Client(origin, opts) + : new Pool(origin, opts) +} + +class Agent extends DispatcherBase { + constructor ({ factory = defaultFactory, maxRedirections = 0, connect, ...options } = {}) { + super() + + if (typeof factory !== 'function') { + throw new InvalidArgumentError('factory must be a function.') + } + + if (connect != null && typeof connect !== 'function' && typeof connect !== 'object') { + throw new InvalidArgumentError('connect must be a function or an object') + } + + if (!Number.isInteger(maxRedirections) || maxRedirections < 0) { + throw new InvalidArgumentError('maxRedirections must be a positive number') + } + + if (connect && typeof connect !== 'function') { + connect = { ...connect } + } + + this[kInterceptors] = options.interceptors && options.interceptors.Agent && Array.isArray(options.interceptors.Agent) + ? options.interceptors.Agent + : [createRedirectInterceptor({ maxRedirections })] + + this[kOptions] = { ...util.deepClone(options), connect } + this[kOptions].interceptors = options.interceptors + ? { ...options.interceptors } + : undefined + this[kMaxRedirections] = maxRedirections + this[kFactory] = factory + this[kClients] = new Map() + this[kFinalizer] = new FinalizationRegistry(/* istanbul ignore next: gc is undeterministic */ key => { + const ref = this[kClients].get(key) + if (ref !== undefined && ref.deref() === undefined) { + this[kClients].delete(key) + } + }) + + const agent = this + + this[kOnDrain] = (origin, targets) => { + agent.emit('drain', origin, [agent, ...targets]) + } + + this[kOnConnect] = (origin, targets) => { + agent.emit('connect', origin, [agent, ...targets]) + } + + this[kOnDisconnect] = (origin, targets, err) => { + agent.emit('disconnect', origin, [agent, ...targets], err) + } + + this[kOnConnectionError] = (origin, targets, err) => { + agent.emit('connectionError', origin, [agent, ...targets], err) + } + } + + get [kRunning] () { + let ret = 0 + for (const ref of this[kClients].values()) { + const client = ref.deref() + /* istanbul ignore next: gc is undeterministic */ + if (client) { + ret += client[kRunning] + } + } + return ret + } + + [kDispatch] (opts, handler) { + let key + if (opts.origin && (typeof opts.origin === 'string' || opts.origin instanceof URL)) { + key = String(opts.origin) + } else { + throw new InvalidArgumentError('opts.origin must be a non-empty string or URL.') + } + + const ref = this[kClients].get(key) + + let dispatcher = ref ? ref.deref() : null + if (!dispatcher) { + dispatcher = this[kFactory](opts.origin, this[kOptions]) + .on('drain', this[kOnDrain]) + .on('connect', this[kOnConnect]) + .on('disconnect', this[kOnDisconnect]) + .on('connectionError', this[kOnConnectionError]) + + this[kClients].set(key, new WeakRef(dispatcher)) + this[kFinalizer].register(dispatcher, key) + } + + return dispatcher.dispatch(opts, handler) + } + + async [kClose] () { + const closePromises = [] + for (const ref of this[kClients].values()) { + const client = ref.deref() + /* istanbul ignore else: gc is undeterministic */ + if (client) { + closePromises.push(client.close()) + } + } + + await Promise.all(closePromises) + } + + async [kDestroy] (err) { + const destroyPromises = [] + for (const ref of this[kClients].values()) { + const client = ref.deref() + /* istanbul ignore else: gc is undeterministic */ + if (client) { + destroyPromises.push(client.destroy(err)) + } + } + + await Promise.all(destroyPromises) + } +} + +module.exports = Agent + + +/***/ }), + +/***/ 7032: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +const { addAbortListener } = __nccwpck_require__(3983) +const { RequestAbortedError } = __nccwpck_require__(8045) + +const kListener = Symbol('kListener') +const kSignal = Symbol('kSignal') + +function abort (self) { + if (self.abort) { + self.abort() + } else { + self.onError(new RequestAbortedError()) + } +} + +function addSignal (self, signal) { + self[kSignal] = null + self[kListener] = null + + if (!signal) { + return + } + + if (signal.aborted) { + abort(self) + return + } + + self[kSignal] = signal + self[kListener] = () => { + abort(self) + } + + addAbortListener(self[kSignal], self[kListener]) +} + +function removeSignal (self) { + if (!self[kSignal]) { + return + } + + if ('removeEventListener' in self[kSignal]) { + self[kSignal].removeEventListener('abort', self[kListener]) + } else { + self[kSignal].removeListener('abort', self[kListener]) + } + + self[kSignal] = null + self[kListener] = null +} + +module.exports = { + addSignal, + removeSignal +} + + +/***/ }), + +/***/ 9744: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { AsyncResource } = __nccwpck_require__(852) +const { InvalidArgumentError, RequestAbortedError, SocketError } = __nccwpck_require__(8045) +const util = __nccwpck_require__(3983) +const { addSignal, removeSignal } = __nccwpck_require__(7032) + +class ConnectHandler extends AsyncResource { + constructor (opts, callback) { + if (!opts || typeof opts !== 'object') { + throw new InvalidArgumentError('invalid opts') + } + + if (typeof callback !== 'function') { + throw new InvalidArgumentError('invalid callback') + } + + const { signal, opaque, responseHeaders } = opts + + if (signal && typeof signal.on !== 'function' && typeof signal.addEventListener !== 'function') { + throw new InvalidArgumentError('signal must be an EventEmitter or EventTarget') + } + + super('UNDICI_CONNECT') + + this.opaque = opaque || null + this.responseHeaders = responseHeaders || null + this.callback = callback + this.abort = null + + addSignal(this, signal) + } + + onConnect (abort, context) { + if (!this.callback) { + throw new RequestAbortedError() + } + + this.abort = abort + this.context = context + } + + onHeaders () { + throw new SocketError('bad connect', null) + } + + onUpgrade (statusCode, rawHeaders, socket) { + const { callback, opaque, context } = this + + removeSignal(this) + + this.callback = null + + let headers = rawHeaders + // Indicates is an HTTP2Session + if (headers != null) { + headers = this.responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders) + } + + this.runInAsyncScope(callback, null, null, { + statusCode, + headers, + socket, + opaque, + context + }) + } + + onError (err) { + const { callback, opaque } = this + + removeSignal(this) + + if (callback) { + this.callback = null + queueMicrotask(() => { + this.runInAsyncScope(callback, null, err, { opaque }) + }) + } + } +} + +function connect (opts, callback) { + if (callback === undefined) { + return new Promise((resolve, reject) => { + connect.call(this, opts, (err, data) => { + return err ? reject(err) : resolve(data) + }) + }) + } + + try { + const connectHandler = new ConnectHandler(opts, callback) + this.dispatch({ ...opts, method: 'CONNECT' }, connectHandler) + } catch (err) { + if (typeof callback !== 'function') { + throw err + } + const opaque = opts && opts.opaque + queueMicrotask(() => callback(err, { opaque })) + } +} + +module.exports = connect + + +/***/ }), + +/***/ 8752: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { + Readable, + Duplex, + PassThrough +} = __nccwpck_require__(2781) +const { + InvalidArgumentError, + InvalidReturnValueError, + RequestAbortedError +} = __nccwpck_require__(8045) +const util = __nccwpck_require__(3983) +const { AsyncResource } = __nccwpck_require__(852) +const { addSignal, removeSignal } = __nccwpck_require__(7032) +const assert = __nccwpck_require__(9491) + +const kResume = Symbol('resume') + +class PipelineRequest extends Readable { + constructor () { + super({ autoDestroy: true }) + + this[kResume] = null + } + + _read () { + const { [kResume]: resume } = this + + if (resume) { + this[kResume] = null + resume() + } + } + + _destroy (err, callback) { + this._read() + + callback(err) + } +} + +class PipelineResponse extends Readable { + constructor (resume) { + super({ autoDestroy: true }) + this[kResume] = resume + } + + _read () { + this[kResume]() + } + + _destroy (err, callback) { + if (!err && !this._readableState.endEmitted) { + err = new RequestAbortedError() + } + + callback(err) + } +} + +class PipelineHandler extends AsyncResource { + constructor (opts, handler) { + if (!opts || typeof opts !== 'object') { + throw new InvalidArgumentError('invalid opts') + } + + if (typeof handler !== 'function') { + throw new InvalidArgumentError('invalid handler') + } + + const { signal, method, opaque, onInfo, responseHeaders } = opts + + if (signal && typeof signal.on !== 'function' && typeof signal.addEventListener !== 'function') { + throw new InvalidArgumentError('signal must be an EventEmitter or EventTarget') + } + + if (method === 'CONNECT') { + throw new InvalidArgumentError('invalid method') + } + + if (onInfo && typeof onInfo !== 'function') { + throw new InvalidArgumentError('invalid onInfo callback') + } + + super('UNDICI_PIPELINE') + + this.opaque = opaque || null + this.responseHeaders = responseHeaders || null + this.handler = handler + this.abort = null + this.context = null + this.onInfo = onInfo || null + + this.req = new PipelineRequest().on('error', util.nop) + + this.ret = new Duplex({ + readableObjectMode: opts.objectMode, + autoDestroy: true, + read: () => { + const { body } = this + + if (body && body.resume) { + body.resume() + } + }, + write: (chunk, encoding, callback) => { + const { req } = this + + if (req.push(chunk, encoding) || req._readableState.destroyed) { + callback() + } else { + req[kResume] = callback + } + }, + destroy: (err, callback) => { + const { body, req, res, ret, abort } = this + + if (!err && !ret._readableState.endEmitted) { + err = new RequestAbortedError() + } + + if (abort && err) { + abort() + } + + util.destroy(body, err) + util.destroy(req, err) + util.destroy(res, err) + + removeSignal(this) + + callback(err) + } + }).on('prefinish', () => { + const { req } = this + + // Node < 15 does not call _final in same tick. + req.push(null) + }) + + this.res = null + + addSignal(this, signal) + } + + onConnect (abort, context) { + const { ret, res } = this + + assert(!res, 'pipeline cannot be retried') + + if (ret.destroyed) { + throw new RequestAbortedError() + } + + this.abort = abort + this.context = context + } + + onHeaders (statusCode, rawHeaders, resume) { + const { opaque, handler, context } = this + + if (statusCode < 200) { + if (this.onInfo) { + const headers = this.responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders) + this.onInfo({ statusCode, headers }) + } + return + } + + this.res = new PipelineResponse(resume) + + let body + try { + this.handler = null + const headers = this.responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders) + body = this.runInAsyncScope(handler, null, { + statusCode, + headers, + opaque, + body: this.res, + context + }) + } catch (err) { + this.res.on('error', util.nop) + throw err + } + + if (!body || typeof body.on !== 'function') { + throw new InvalidReturnValueError('expected Readable') + } + + body + .on('data', (chunk) => { + const { ret, body } = this + + if (!ret.push(chunk) && body.pause) { + body.pause() + } + }) + .on('error', (err) => { + const { ret } = this + + util.destroy(ret, err) + }) + .on('end', () => { + const { ret } = this + + ret.push(null) + }) + .on('close', () => { + const { ret } = this + + if (!ret._readableState.ended) { + util.destroy(ret, new RequestAbortedError()) + } + }) + + this.body = body + } + + onData (chunk) { + const { res } = this + return res.push(chunk) + } + + onComplete (trailers) { + const { res } = this + res.push(null) + } + + onError (err) { + const { ret } = this + this.handler = null + util.destroy(ret, err) + } +} + +function pipeline (opts, handler) { + try { + const pipelineHandler = new PipelineHandler(opts, handler) + this.dispatch({ ...opts, body: pipelineHandler.req }, pipelineHandler) + return pipelineHandler.ret + } catch (err) { + return new PassThrough().destroy(err) + } +} + +module.exports = pipeline + + +/***/ }), + +/***/ 5448: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const Readable = __nccwpck_require__(3858) +const { + InvalidArgumentError, + RequestAbortedError +} = __nccwpck_require__(8045) +const util = __nccwpck_require__(3983) +const { getResolveErrorBodyCallback } = __nccwpck_require__(7474) +const { AsyncResource } = __nccwpck_require__(852) +const { addSignal, removeSignal } = __nccwpck_require__(7032) + +class RequestHandler extends AsyncResource { + constructor (opts, callback) { + if (!opts || typeof opts !== 'object') { + throw new InvalidArgumentError('invalid opts') + } + + const { signal, method, opaque, body, onInfo, responseHeaders, throwOnError, highWaterMark } = opts + + try { + if (typeof callback !== 'function') { + throw new InvalidArgumentError('invalid callback') + } + + if (highWaterMark && (typeof highWaterMark !== 'number' || highWaterMark < 0)) { + throw new InvalidArgumentError('invalid highWaterMark') + } + + if (signal && typeof signal.on !== 'function' && typeof signal.addEventListener !== 'function') { + throw new InvalidArgumentError('signal must be an EventEmitter or EventTarget') + } + + if (method === 'CONNECT') { + throw new InvalidArgumentError('invalid method') + } + + if (onInfo && typeof onInfo !== 'function') { + throw new InvalidArgumentError('invalid onInfo callback') + } + + super('UNDICI_REQUEST') + } catch (err) { + if (util.isStream(body)) { + util.destroy(body.on('error', util.nop), err) + } + throw err + } + + this.responseHeaders = responseHeaders || null + this.opaque = opaque || null + this.callback = callback + this.res = null + this.abort = null + this.body = body + this.trailers = {} + this.context = null + this.onInfo = onInfo || null + this.throwOnError = throwOnError + this.highWaterMark = highWaterMark + + if (util.isStream(body)) { + body.on('error', (err) => { + this.onError(err) + }) + } + + addSignal(this, signal) + } + + onConnect (abort, context) { + if (!this.callback) { + throw new RequestAbortedError() + } + + this.abort = abort + this.context = context + } + + onHeaders (statusCode, rawHeaders, resume, statusMessage) { + const { callback, opaque, abort, context, responseHeaders, highWaterMark } = this + + const headers = responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders) + + if (statusCode < 200) { + if (this.onInfo) { + this.onInfo({ statusCode, headers }) + } + return + } + + const parsedHeaders = responseHeaders === 'raw' ? util.parseHeaders(rawHeaders) : headers + const contentType = parsedHeaders['content-type'] + const body = new Readable({ resume, abort, contentType, highWaterMark }) + + this.callback = null + this.res = body + if (callback !== null) { + if (this.throwOnError && statusCode >= 400) { + this.runInAsyncScope(getResolveErrorBodyCallback, null, + { callback, body, contentType, statusCode, statusMessage, headers } + ) + } else { + this.runInAsyncScope(callback, null, null, { + statusCode, + headers, + trailers: this.trailers, + opaque, + body, + context + }) + } + } + } + + onData (chunk) { + const { res } = this + return res.push(chunk) + } + + onComplete (trailers) { + const { res } = this + + removeSignal(this) + + util.parseHeaders(trailers, this.trailers) + + res.push(null) + } + + onError (err) { + const { res, callback, body, opaque } = this + + removeSignal(this) + + if (callback) { + // TODO: Does this need queueMicrotask? + this.callback = null + queueMicrotask(() => { + this.runInAsyncScope(callback, null, err, { opaque }) + }) + } + + if (res) { + this.res = null + // Ensure all queued handlers are invoked before destroying res. + queueMicrotask(() => { + util.destroy(res, err) + }) + } + + if (body) { + this.body = null + util.destroy(body, err) + } + } +} + +function request (opts, callback) { + if (callback === undefined) { + return new Promise((resolve, reject) => { + request.call(this, opts, (err, data) => { + return err ? reject(err) : resolve(data) + }) + }) + } + + try { + this.dispatch(opts, new RequestHandler(opts, callback)) + } catch (err) { + if (typeof callback !== 'function') { + throw err + } + const opaque = opts && opts.opaque + queueMicrotask(() => callback(err, { opaque })) + } +} + +module.exports = request + + +/***/ }), + +/***/ 5395: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { finished, PassThrough } = __nccwpck_require__(2781) +const { + InvalidArgumentError, + InvalidReturnValueError, + RequestAbortedError +} = __nccwpck_require__(8045) +const util = __nccwpck_require__(3983) +const { getResolveErrorBodyCallback } = __nccwpck_require__(7474) +const { AsyncResource } = __nccwpck_require__(852) +const { addSignal, removeSignal } = __nccwpck_require__(7032) + +class StreamHandler extends AsyncResource { + constructor (opts, factory, callback) { + if (!opts || typeof opts !== 'object') { + throw new InvalidArgumentError('invalid opts') + } + + const { signal, method, opaque, body, onInfo, responseHeaders, throwOnError } = opts + + try { + if (typeof callback !== 'function') { + throw new InvalidArgumentError('invalid callback') + } + + if (typeof factory !== 'function') { + throw new InvalidArgumentError('invalid factory') + } + + if (signal && typeof signal.on !== 'function' && typeof signal.addEventListener !== 'function') { + throw new InvalidArgumentError('signal must be an EventEmitter or EventTarget') + } + + if (method === 'CONNECT') { + throw new InvalidArgumentError('invalid method') + } + + if (onInfo && typeof onInfo !== 'function') { + throw new InvalidArgumentError('invalid onInfo callback') + } + + super('UNDICI_STREAM') + } catch (err) { + if (util.isStream(body)) { + util.destroy(body.on('error', util.nop), err) + } + throw err + } + + this.responseHeaders = responseHeaders || null + this.opaque = opaque || null + this.factory = factory + this.callback = callback + this.res = null + this.abort = null + this.context = null + this.trailers = null + this.body = body + this.onInfo = onInfo || null + this.throwOnError = throwOnError || false + + if (util.isStream(body)) { + body.on('error', (err) => { + this.onError(err) + }) + } + + addSignal(this, signal) + } + + onConnect (abort, context) { + if (!this.callback) { + throw new RequestAbortedError() + } + + this.abort = abort + this.context = context + } + + onHeaders (statusCode, rawHeaders, resume, statusMessage) { + const { factory, opaque, context, callback, responseHeaders } = this + + const headers = responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders) + + if (statusCode < 200) { + if (this.onInfo) { + this.onInfo({ statusCode, headers }) + } + return + } + + this.factory = null + + let res + + if (this.throwOnError && statusCode >= 400) { + const parsedHeaders = responseHeaders === 'raw' ? util.parseHeaders(rawHeaders) : headers + const contentType = parsedHeaders['content-type'] + res = new PassThrough() + + this.callback = null + this.runInAsyncScope(getResolveErrorBodyCallback, null, + { callback, body: res, contentType, statusCode, statusMessage, headers } + ) + } else { + if (factory === null) { + return + } + + res = this.runInAsyncScope(factory, null, { + statusCode, + headers, + opaque, + context + }) + + if ( + !res || + typeof res.write !== 'function' || + typeof res.end !== 'function' || + typeof res.on !== 'function' + ) { + throw new InvalidReturnValueError('expected Writable') + } + + // TODO: Avoid finished. It registers an unnecessary amount of listeners. + finished(res, { readable: false }, (err) => { + const { callback, res, opaque, trailers, abort } = this + + this.res = null + if (err || !res.readable) { + util.destroy(res, err) + } + + this.callback = null + this.runInAsyncScope(callback, null, err || null, { opaque, trailers }) + + if (err) { + abort() + } + }) + } + + res.on('drain', resume) + + this.res = res + + const needDrain = res.writableNeedDrain !== undefined + ? res.writableNeedDrain + : res._writableState && res._writableState.needDrain + + return needDrain !== true + } + + onData (chunk) { + const { res } = this + + return res ? res.write(chunk) : true + } + + onComplete (trailers) { + const { res } = this + + removeSignal(this) + + if (!res) { + return + } + + this.trailers = util.parseHeaders(trailers) + + res.end() + } + + onError (err) { + const { res, callback, opaque, body } = this + + removeSignal(this) + + this.factory = null + + if (res) { + this.res = null + util.destroy(res, err) + } else if (callback) { + this.callback = null + queueMicrotask(() => { + this.runInAsyncScope(callback, null, err, { opaque }) + }) + } + + if (body) { + this.body = null + util.destroy(body, err) + } + } +} + +function stream (opts, factory, callback) { + if (callback === undefined) { + return new Promise((resolve, reject) => { + stream.call(this, opts, factory, (err, data) => { + return err ? reject(err) : resolve(data) + }) + }) + } + + try { + this.dispatch(opts, new StreamHandler(opts, factory, callback)) + } catch (err) { + if (typeof callback !== 'function') { + throw err + } + const opaque = opts && opts.opaque + queueMicrotask(() => callback(err, { opaque })) + } +} + +module.exports = stream + + +/***/ }), + +/***/ 6923: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { InvalidArgumentError, RequestAbortedError, SocketError } = __nccwpck_require__(8045) +const { AsyncResource } = __nccwpck_require__(852) +const util = __nccwpck_require__(3983) +const { addSignal, removeSignal } = __nccwpck_require__(7032) +const assert = __nccwpck_require__(9491) + +class UpgradeHandler extends AsyncResource { + constructor (opts, callback) { + if (!opts || typeof opts !== 'object') { + throw new InvalidArgumentError('invalid opts') + } + + if (typeof callback !== 'function') { + throw new InvalidArgumentError('invalid callback') + } + + const { signal, opaque, responseHeaders } = opts + + if (signal && typeof signal.on !== 'function' && typeof signal.addEventListener !== 'function') { + throw new InvalidArgumentError('signal must be an EventEmitter or EventTarget') + } + + super('UNDICI_UPGRADE') + + this.responseHeaders = responseHeaders || null + this.opaque = opaque || null + this.callback = callback + this.abort = null + this.context = null + + addSignal(this, signal) + } + + onConnect (abort, context) { + if (!this.callback) { + throw new RequestAbortedError() + } + + this.abort = abort + this.context = null + } + + onHeaders () { + throw new SocketError('bad upgrade', null) + } + + onUpgrade (statusCode, rawHeaders, socket) { + const { callback, opaque, context } = this + + assert.strictEqual(statusCode, 101) + + removeSignal(this) + + this.callback = null + const headers = this.responseHeaders === 'raw' ? util.parseRawHeaders(rawHeaders) : util.parseHeaders(rawHeaders) + this.runInAsyncScope(callback, null, null, { + headers, + socket, + opaque, + context + }) + } + + onError (err) { + const { callback, opaque } = this + + removeSignal(this) + + if (callback) { + this.callback = null + queueMicrotask(() => { + this.runInAsyncScope(callback, null, err, { opaque }) + }) + } + } +} + +function upgrade (opts, callback) { + if (callback === undefined) { + return new Promise((resolve, reject) => { + upgrade.call(this, opts, (err, data) => { + return err ? reject(err) : resolve(data) + }) + }) + } + + try { + const upgradeHandler = new UpgradeHandler(opts, callback) + this.dispatch({ + ...opts, + method: opts.method || 'GET', + upgrade: opts.protocol || 'Websocket' + }, upgradeHandler) + } catch (err) { + if (typeof callback !== 'function') { + throw err + } + const opaque = opts && opts.opaque + queueMicrotask(() => callback(err, { opaque })) + } +} + +module.exports = upgrade + + +/***/ }), + +/***/ 4059: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +module.exports.request = __nccwpck_require__(5448) +module.exports.stream = __nccwpck_require__(5395) +module.exports.pipeline = __nccwpck_require__(8752) +module.exports.upgrade = __nccwpck_require__(6923) +module.exports.connect = __nccwpck_require__(9744) + + +/***/ }), + +/***/ 3858: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; +// Ported from https://github.com/nodejs/undici/pull/907 + + + +const assert = __nccwpck_require__(9491) +const { Readable } = __nccwpck_require__(2781) +const { RequestAbortedError, NotSupportedError, InvalidArgumentError } = __nccwpck_require__(8045) +const util = __nccwpck_require__(3983) +const { ReadableStreamFrom, toUSVString } = __nccwpck_require__(3983) + +let Blob + +const kConsume = Symbol('kConsume') +const kReading = Symbol('kReading') +const kBody = Symbol('kBody') +const kAbort = Symbol('abort') +const kContentType = Symbol('kContentType') + +module.exports = class BodyReadable extends Readable { + constructor ({ + resume, + abort, + contentType = '', + highWaterMark = 64 * 1024 // Same as nodejs fs streams. + }) { + super({ + autoDestroy: true, + read: resume, + highWaterMark + }) + + this._readableState.dataEmitted = false + + this[kAbort] = abort + this[kConsume] = null + this[kBody] = null + this[kContentType] = contentType + + // Is stream being consumed through Readable API? + // This is an optimization so that we avoid checking + // for 'data' and 'readable' listeners in the hot path + // inside push(). + this[kReading] = false + } + + destroy (err) { + if (this.destroyed) { + // Node < 16 + return this + } + + if (!err && !this._readableState.endEmitted) { + err = new RequestAbortedError() + } + + if (err) { + this[kAbort]() + } + + return super.destroy(err) + } + + emit (ev, ...args) { + if (ev === 'data') { + // Node < 16.7 + this._readableState.dataEmitted = true + } else if (ev === 'error') { + // Node < 16 + this._readableState.errorEmitted = true + } + return super.emit(ev, ...args) + } + + on (ev, ...args) { + if (ev === 'data' || ev === 'readable') { + this[kReading] = true + } + return super.on(ev, ...args) + } + + addListener (ev, ...args) { + return this.on(ev, ...args) + } + + off (ev, ...args) { + const ret = super.off(ev, ...args) + if (ev === 'data' || ev === 'readable') { + this[kReading] = ( + this.listenerCount('data') > 0 || + this.listenerCount('readable') > 0 + ) + } + return ret + } + + removeListener (ev, ...args) { + return this.off(ev, ...args) + } + + push (chunk) { + if (this[kConsume] && chunk !== null && this.readableLength === 0) { + consumePush(this[kConsume], chunk) + return this[kReading] ? super.push(chunk) : true + } + return super.push(chunk) + } + + // https://fetch.spec.whatwg.org/#dom-body-text + async text () { + return consume(this, 'text') + } + + // https://fetch.spec.whatwg.org/#dom-body-json + async json () { + return consume(this, 'json') + } + + // https://fetch.spec.whatwg.org/#dom-body-blob + async blob () { + return consume(this, 'blob') + } + + // https://fetch.spec.whatwg.org/#dom-body-arraybuffer + async arrayBuffer () { + return consume(this, 'arrayBuffer') + } + + // https://fetch.spec.whatwg.org/#dom-body-formdata + async formData () { + // TODO: Implement. + throw new NotSupportedError() + } + + // https://fetch.spec.whatwg.org/#dom-body-bodyused + get bodyUsed () { + return util.isDisturbed(this) + } + + // https://fetch.spec.whatwg.org/#dom-body-body + get body () { + if (!this[kBody]) { + this[kBody] = ReadableStreamFrom(this) + if (this[kConsume]) { + // TODO: Is this the best way to force a lock? + this[kBody].getReader() // Ensure stream is locked. + assert(this[kBody].locked) + } + } + return this[kBody] + } + + async dump (opts) { + let limit = opts && Number.isFinite(opts.limit) ? opts.limit : 262144 + const signal = opts && opts.signal + const abortFn = () => { + this.destroy() + } + let signalListenerCleanup + if (signal) { + if (typeof signal !== 'object' || !('aborted' in signal)) { + throw new InvalidArgumentError('signal must be an AbortSignal') + } + util.throwIfAborted(signal) + signalListenerCleanup = util.addAbortListener(signal, abortFn) + } + try { + for await (const chunk of this) { + util.throwIfAborted(signal) + limit -= Buffer.byteLength(chunk) + if (limit < 0) { + return + } + } + } catch { + util.throwIfAborted(signal) + } finally { + if (typeof signalListenerCleanup === 'function') { + signalListenerCleanup() + } else if (signalListenerCleanup) { + signalListenerCleanup[Symbol.dispose]() + } + } + } +} + +// https://streams.spec.whatwg.org/#readablestream-locked +function isLocked (self) { + // Consume is an implicit lock. + return (self[kBody] && self[kBody].locked === true) || self[kConsume] +} + +// https://fetch.spec.whatwg.org/#body-unusable +function isUnusable (self) { + return util.isDisturbed(self) || isLocked(self) +} + +async function consume (stream, type) { + if (isUnusable(stream)) { + throw new TypeError('unusable') + } + + assert(!stream[kConsume]) + + return new Promise((resolve, reject) => { + stream[kConsume] = { + type, + stream, + resolve, + reject, + length: 0, + body: [] + } + + stream + .on('error', function (err) { + consumeFinish(this[kConsume], err) + }) + .on('close', function () { + if (this[kConsume].body !== null) { + consumeFinish(this[kConsume], new RequestAbortedError()) + } + }) + + process.nextTick(consumeStart, stream[kConsume]) + }) +} + +function consumeStart (consume) { + if (consume.body === null) { + return + } + + const { _readableState: state } = consume.stream + + for (const chunk of state.buffer) { + consumePush(consume, chunk) + } + + if (state.endEmitted) { + consumeEnd(this[kConsume]) + } else { + consume.stream.on('end', function () { + consumeEnd(this[kConsume]) + }) + } + + consume.stream.resume() + + while (consume.stream.read() != null) { + // Loop + } +} + +function consumeEnd (consume) { + const { type, body, resolve, stream, length } = consume + + try { + if (type === 'text') { + resolve(toUSVString(Buffer.concat(body))) + } else if (type === 'json') { + resolve(JSON.parse(Buffer.concat(body))) + } else if (type === 'arrayBuffer') { + const dst = new Uint8Array(length) + + let pos = 0 + for (const buf of body) { + dst.set(buf, pos) + pos += buf.byteLength + } + + resolve(dst.buffer) + } else if (type === 'blob') { + if (!Blob) { + Blob = (__nccwpck_require__(4300).Blob) + } + resolve(new Blob(body, { type: stream[kContentType] })) + } + + consumeFinish(consume) + } catch (err) { + stream.destroy(err) + } +} + +function consumePush (consume, chunk) { + consume.length += chunk.length + consume.body.push(chunk) +} + +function consumeFinish (consume, err) { + if (consume.body === null) { + return + } + + if (err) { + consume.reject(err) + } else { + consume.resolve() + } + + consume.type = null + consume.stream = null + consume.resolve = null + consume.reject = null + consume.length = 0 + consume.body = null +} + + +/***/ }), + +/***/ 7474: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +const assert = __nccwpck_require__(9491) +const { + ResponseStatusCodeError +} = __nccwpck_require__(8045) +const { toUSVString } = __nccwpck_require__(3983) + +async function getResolveErrorBodyCallback ({ callback, body, contentType, statusCode, statusMessage, headers }) { + assert(body) + + let chunks = [] + let limit = 0 + + for await (const chunk of body) { + chunks.push(chunk) + limit += chunk.length + if (limit > 128 * 1024) { + chunks = null + break + } + } + + if (statusCode === 204 || !contentType || !chunks) { + process.nextTick(callback, new ResponseStatusCodeError(`Response status code ${statusCode}${statusMessage ? `: ${statusMessage}` : ''}`, statusCode, headers)) + return + } + + try { + if (contentType.startsWith('application/json')) { + const payload = JSON.parse(toUSVString(Buffer.concat(chunks))) + process.nextTick(callback, new ResponseStatusCodeError(`Response status code ${statusCode}${statusMessage ? `: ${statusMessage}` : ''}`, statusCode, headers, payload)) + return + } + + if (contentType.startsWith('text/')) { + const payload = toUSVString(Buffer.concat(chunks)) + process.nextTick(callback, new ResponseStatusCodeError(`Response status code ${statusCode}${statusMessage ? `: ${statusMessage}` : ''}`, statusCode, headers, payload)) + return + } + } catch (err) { + // Process in a fallback if error + } + + process.nextTick(callback, new ResponseStatusCodeError(`Response status code ${statusCode}${statusMessage ? `: ${statusMessage}` : ''}`, statusCode, headers)) +} + +module.exports = { getResolveErrorBodyCallback } + + +/***/ }), + +/***/ 7931: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { + BalancedPoolMissingUpstreamError, + InvalidArgumentError +} = __nccwpck_require__(8045) +const { + PoolBase, + kClients, + kNeedDrain, + kAddClient, + kRemoveClient, + kGetDispatcher +} = __nccwpck_require__(3198) +const Pool = __nccwpck_require__(4634) +const { kUrl, kInterceptors } = __nccwpck_require__(2785) +const { parseOrigin } = __nccwpck_require__(3983) +const kFactory = Symbol('factory') + +const kOptions = Symbol('options') +const kGreatestCommonDivisor = Symbol('kGreatestCommonDivisor') +const kCurrentWeight = Symbol('kCurrentWeight') +const kIndex = Symbol('kIndex') +const kWeight = Symbol('kWeight') +const kMaxWeightPerServer = Symbol('kMaxWeightPerServer') +const kErrorPenalty = Symbol('kErrorPenalty') + +function getGreatestCommonDivisor (a, b) { + if (b === 0) return a + return getGreatestCommonDivisor(b, a % b) +} + +function defaultFactory (origin, opts) { + return new Pool(origin, opts) +} + +class BalancedPool extends PoolBase { + constructor (upstreams = [], { factory = defaultFactory, ...opts } = {}) { + super() + + this[kOptions] = opts + this[kIndex] = -1 + this[kCurrentWeight] = 0 + + this[kMaxWeightPerServer] = this[kOptions].maxWeightPerServer || 100 + this[kErrorPenalty] = this[kOptions].errorPenalty || 15 + + if (!Array.isArray(upstreams)) { + upstreams = [upstreams] + } + + if (typeof factory !== 'function') { + throw new InvalidArgumentError('factory must be a function.') + } + + this[kInterceptors] = opts.interceptors && opts.interceptors.BalancedPool && Array.isArray(opts.interceptors.BalancedPool) + ? opts.interceptors.BalancedPool + : [] + this[kFactory] = factory + + for (const upstream of upstreams) { + this.addUpstream(upstream) + } + this._updateBalancedPoolStats() + } + + addUpstream (upstream) { + const upstreamOrigin = parseOrigin(upstream).origin + + if (this[kClients].find((pool) => ( + pool[kUrl].origin === upstreamOrigin && + pool.closed !== true && + pool.destroyed !== true + ))) { + return this + } + const pool = this[kFactory](upstreamOrigin, Object.assign({}, this[kOptions])) + + this[kAddClient](pool) + pool.on('connect', () => { + pool[kWeight] = Math.min(this[kMaxWeightPerServer], pool[kWeight] + this[kErrorPenalty]) + }) + + pool.on('connectionError', () => { + pool[kWeight] = Math.max(1, pool[kWeight] - this[kErrorPenalty]) + this._updateBalancedPoolStats() + }) + + pool.on('disconnect', (...args) => { + const err = args[2] + if (err && err.code === 'UND_ERR_SOCKET') { + // decrease the weight of the pool. + pool[kWeight] = Math.max(1, pool[kWeight] - this[kErrorPenalty]) + this._updateBalancedPoolStats() + } + }) + + for (const client of this[kClients]) { + client[kWeight] = this[kMaxWeightPerServer] + } + + this._updateBalancedPoolStats() + + return this + } + + _updateBalancedPoolStats () { + this[kGreatestCommonDivisor] = this[kClients].map(p => p[kWeight]).reduce(getGreatestCommonDivisor, 0) + } + + removeUpstream (upstream) { + const upstreamOrigin = parseOrigin(upstream).origin + + const pool = this[kClients].find((pool) => ( + pool[kUrl].origin === upstreamOrigin && + pool.closed !== true && + pool.destroyed !== true + )) + + if (pool) { + this[kRemoveClient](pool) + } + + return this + } + + get upstreams () { + return this[kClients] + .filter(dispatcher => dispatcher.closed !== true && dispatcher.destroyed !== true) + .map((p) => p[kUrl].origin) + } + + [kGetDispatcher] () { + // We validate that pools is greater than 0, + // otherwise we would have to wait until an upstream + // is added, which might never happen. + if (this[kClients].length === 0) { + throw new BalancedPoolMissingUpstreamError() + } + + const dispatcher = this[kClients].find(dispatcher => ( + !dispatcher[kNeedDrain] && + dispatcher.closed !== true && + dispatcher.destroyed !== true + )) + + if (!dispatcher) { + return + } + + const allClientsBusy = this[kClients].map(pool => pool[kNeedDrain]).reduce((a, b) => a && b, true) + + if (allClientsBusy) { + return + } + + let counter = 0 + + let maxWeightIndex = this[kClients].findIndex(pool => !pool[kNeedDrain]) + + while (counter++ < this[kClients].length) { + this[kIndex] = (this[kIndex] + 1) % this[kClients].length + const pool = this[kClients][this[kIndex]] + + // find pool index with the largest weight + if (pool[kWeight] > this[kClients][maxWeightIndex][kWeight] && !pool[kNeedDrain]) { + maxWeightIndex = this[kIndex] + } + + // decrease the current weight every `this[kClients].length`. + if (this[kIndex] === 0) { + // Set the current weight to the next lower weight. + this[kCurrentWeight] = this[kCurrentWeight] - this[kGreatestCommonDivisor] + + if (this[kCurrentWeight] <= 0) { + this[kCurrentWeight] = this[kMaxWeightPerServer] + } + } + if (pool[kWeight] >= this[kCurrentWeight] && (!pool[kNeedDrain])) { + return pool + } + } + + this[kCurrentWeight] = this[kClients][maxWeightIndex][kWeight] + this[kIndex] = maxWeightIndex + return this[kClients][maxWeightIndex] + } +} + +module.exports = BalancedPool + + +/***/ }), + +/***/ 6101: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { kConstruct } = __nccwpck_require__(9174) +const { urlEquals, fieldValues: getFieldValues } = __nccwpck_require__(2396) +const { kEnumerableProperty, isDisturbed } = __nccwpck_require__(3983) +const { kHeadersList } = __nccwpck_require__(2785) +const { webidl } = __nccwpck_require__(1744) +const { Response, cloneResponse } = __nccwpck_require__(7823) +const { Request } = __nccwpck_require__(8359) +const { kState, kHeaders, kGuard, kRealm } = __nccwpck_require__(5861) +const { fetching } = __nccwpck_require__(4881) +const { urlIsHttpHttpsScheme, createDeferredPromise, readAllBytes } = __nccwpck_require__(2538) +const assert = __nccwpck_require__(9491) +const { getGlobalDispatcher } = __nccwpck_require__(1892) + +/** + * @see https://w3c.github.io/ServiceWorker/#dfn-cache-batch-operation + * @typedef {Object} CacheBatchOperation + * @property {'delete' | 'put'} type + * @property {any} request + * @property {any} response + * @property {import('../../types/cache').CacheQueryOptions} options + */ + +/** + * @see https://w3c.github.io/ServiceWorker/#dfn-request-response-list + * @typedef {[any, any][]} requestResponseList + */ + +class Cache { + /** + * @see https://w3c.github.io/ServiceWorker/#dfn-relevant-request-response-list + * @type {requestResponseList} + */ + #relevantRequestResponseList + + constructor () { + if (arguments[0] !== kConstruct) { + webidl.illegalConstructor() + } + + this.#relevantRequestResponseList = arguments[1] + } + + async match (request, options = {}) { + webidl.brandCheck(this, Cache) + webidl.argumentLengthCheck(arguments, 1, { header: 'Cache.match' }) + + request = webidl.converters.RequestInfo(request) + options = webidl.converters.CacheQueryOptions(options) + + const p = await this.matchAll(request, options) + + if (p.length === 0) { + return + } + + return p[0] + } + + async matchAll (request = undefined, options = {}) { + webidl.brandCheck(this, Cache) + + if (request !== undefined) request = webidl.converters.RequestInfo(request) + options = webidl.converters.CacheQueryOptions(options) + + // 1. + let r = null + + // 2. + if (request !== undefined) { + if (request instanceof Request) { + // 2.1.1 + r = request[kState] + + // 2.1.2 + if (r.method !== 'GET' && !options.ignoreMethod) { + return [] + } + } else if (typeof request === 'string') { + // 2.2.1 + r = new Request(request)[kState] + } + } + + // 5. + // 5.1 + const responses = [] + + // 5.2 + if (request === undefined) { + // 5.2.1 + for (const requestResponse of this.#relevantRequestResponseList) { + responses.push(requestResponse[1]) + } + } else { // 5.3 + // 5.3.1 + const requestResponses = this.#queryCache(r, options) + + // 5.3.2 + for (const requestResponse of requestResponses) { + responses.push(requestResponse[1]) + } + } + + // 5.4 + // We don't implement CORs so we don't need to loop over the responses, yay! + + // 5.5.1 + const responseList = [] + + // 5.5.2 + for (const response of responses) { + // 5.5.2.1 + const responseObject = new Response(response.body?.source ?? null) + const body = responseObject[kState].body + responseObject[kState] = response + responseObject[kState].body = body + responseObject[kHeaders][kHeadersList] = response.headersList + responseObject[kHeaders][kGuard] = 'immutable' + + responseList.push(responseObject) + } + + // 6. + return Object.freeze(responseList) + } + + async add (request) { + webidl.brandCheck(this, Cache) + webidl.argumentLengthCheck(arguments, 1, { header: 'Cache.add' }) + + request = webidl.converters.RequestInfo(request) + + // 1. + const requests = [request] + + // 2. + const responseArrayPromise = this.addAll(requests) + + // 3. + return await responseArrayPromise + } + + async addAll (requests) { + webidl.brandCheck(this, Cache) + webidl.argumentLengthCheck(arguments, 1, { header: 'Cache.addAll' }) + + requests = webidl.converters['sequence'](requests) + + // 1. + const responsePromises = [] + + // 2. + const requestList = [] + + // 3. + for (const request of requests) { + if (typeof request === 'string') { + continue + } + + // 3.1 + const r = request[kState] + + // 3.2 + if (!urlIsHttpHttpsScheme(r.url) || r.method !== 'GET') { + throw webidl.errors.exception({ + header: 'Cache.addAll', + message: 'Expected http/s scheme when method is not GET.' + }) + } + } + + // 4. + /** @type {ReturnType[]} */ + const fetchControllers = [] + + // 5. + for (const request of requests) { + // 5.1 + const r = new Request(request)[kState] + + // 5.2 + if (!urlIsHttpHttpsScheme(r.url)) { + throw webidl.errors.exception({ + header: 'Cache.addAll', + message: 'Expected http/s scheme.' + }) + } + + // 5.4 + r.initiator = 'fetch' + r.destination = 'subresource' + + // 5.5 + requestList.push(r) + + // 5.6 + const responsePromise = createDeferredPromise() + + // 5.7 + fetchControllers.push(fetching({ + request: r, + dispatcher: getGlobalDispatcher(), + processResponse (response) { + // 1. + if (response.type === 'error' || response.status === 206 || response.status < 200 || response.status > 299) { + responsePromise.reject(webidl.errors.exception({ + header: 'Cache.addAll', + message: 'Received an invalid status code or the request failed.' + })) + } else if (response.headersList.contains('vary')) { // 2. + // 2.1 + const fieldValues = getFieldValues(response.headersList.get('vary')) + + // 2.2 + for (const fieldValue of fieldValues) { + // 2.2.1 + if (fieldValue === '*') { + responsePromise.reject(webidl.errors.exception({ + header: 'Cache.addAll', + message: 'invalid vary field value' + })) + + for (const controller of fetchControllers) { + controller.abort() + } + + return + } + } + } + }, + processResponseEndOfBody (response) { + // 1. + if (response.aborted) { + responsePromise.reject(new DOMException('aborted', 'AbortError')) + return + } + + // 2. + responsePromise.resolve(response) + } + })) + + // 5.8 + responsePromises.push(responsePromise.promise) + } + + // 6. + const p = Promise.all(responsePromises) + + // 7. + const responses = await p + + // 7.1 + const operations = [] + + // 7.2 + let index = 0 + + // 7.3 + for (const response of responses) { + // 7.3.1 + /** @type {CacheBatchOperation} */ + const operation = { + type: 'put', // 7.3.2 + request: requestList[index], // 7.3.3 + response // 7.3.4 + } + + operations.push(operation) // 7.3.5 + + index++ // 7.3.6 + } + + // 7.5 + const cacheJobPromise = createDeferredPromise() + + // 7.6.1 + let errorData = null + + // 7.6.2 + try { + this.#batchCacheOperations(operations) + } catch (e) { + errorData = e + } + + // 7.6.3 + queueMicrotask(() => { + // 7.6.3.1 + if (errorData === null) { + cacheJobPromise.resolve(undefined) + } else { + // 7.6.3.2 + cacheJobPromise.reject(errorData) + } + }) + + // 7.7 + return cacheJobPromise.promise + } + + async put (request, response) { + webidl.brandCheck(this, Cache) + webidl.argumentLengthCheck(arguments, 2, { header: 'Cache.put' }) + + request = webidl.converters.RequestInfo(request) + response = webidl.converters.Response(response) + + // 1. + let innerRequest = null + + // 2. + if (request instanceof Request) { + innerRequest = request[kState] + } else { // 3. + innerRequest = new Request(request)[kState] + } + + // 4. + if (!urlIsHttpHttpsScheme(innerRequest.url) || innerRequest.method !== 'GET') { + throw webidl.errors.exception({ + header: 'Cache.put', + message: 'Expected an http/s scheme when method is not GET' + }) + } + + // 5. + const innerResponse = response[kState] + + // 6. + if (innerResponse.status === 206) { + throw webidl.errors.exception({ + header: 'Cache.put', + message: 'Got 206 status' + }) + } + + // 7. + if (innerResponse.headersList.contains('vary')) { + // 7.1. + const fieldValues = getFieldValues(innerResponse.headersList.get('vary')) + + // 7.2. + for (const fieldValue of fieldValues) { + // 7.2.1 + if (fieldValue === '*') { + throw webidl.errors.exception({ + header: 'Cache.put', + message: 'Got * vary field value' + }) + } + } + } + + // 8. + if (innerResponse.body && (isDisturbed(innerResponse.body.stream) || innerResponse.body.stream.locked)) { + throw webidl.errors.exception({ + header: 'Cache.put', + message: 'Response body is locked or disturbed' + }) + } + + // 9. + const clonedResponse = cloneResponse(innerResponse) + + // 10. + const bodyReadPromise = createDeferredPromise() + + // 11. + if (innerResponse.body != null) { + // 11.1 + const stream = innerResponse.body.stream + + // 11.2 + const reader = stream.getReader() + + // 11.3 + readAllBytes(reader).then(bodyReadPromise.resolve, bodyReadPromise.reject) + } else { + bodyReadPromise.resolve(undefined) + } + + // 12. + /** @type {CacheBatchOperation[]} */ + const operations = [] + + // 13. + /** @type {CacheBatchOperation} */ + const operation = { + type: 'put', // 14. + request: innerRequest, // 15. + response: clonedResponse // 16. + } + + // 17. + operations.push(operation) + + // 19. + const bytes = await bodyReadPromise.promise + + if (clonedResponse.body != null) { + clonedResponse.body.source = bytes + } + + // 19.1 + const cacheJobPromise = createDeferredPromise() + + // 19.2.1 + let errorData = null + + // 19.2.2 + try { + this.#batchCacheOperations(operations) + } catch (e) { + errorData = e + } + + // 19.2.3 + queueMicrotask(() => { + // 19.2.3.1 + if (errorData === null) { + cacheJobPromise.resolve() + } else { // 19.2.3.2 + cacheJobPromise.reject(errorData) + } + }) + + return cacheJobPromise.promise + } + + async delete (request, options = {}) { + webidl.brandCheck(this, Cache) + webidl.argumentLengthCheck(arguments, 1, { header: 'Cache.delete' }) + + request = webidl.converters.RequestInfo(request) + options = webidl.converters.CacheQueryOptions(options) + + /** + * @type {Request} + */ + let r = null + + if (request instanceof Request) { + r = request[kState] + + if (r.method !== 'GET' && !options.ignoreMethod) { + return false + } + } else { + assert(typeof request === 'string') + + r = new Request(request)[kState] + } + + /** @type {CacheBatchOperation[]} */ + const operations = [] + + /** @type {CacheBatchOperation} */ + const operation = { + type: 'delete', + request: r, + options + } + + operations.push(operation) + + const cacheJobPromise = createDeferredPromise() + + let errorData = null + let requestResponses + + try { + requestResponses = this.#batchCacheOperations(operations) + } catch (e) { + errorData = e + } + + queueMicrotask(() => { + if (errorData === null) { + cacheJobPromise.resolve(!!requestResponses?.length) + } else { + cacheJobPromise.reject(errorData) + } + }) + + return cacheJobPromise.promise + } + + /** + * @see https://w3c.github.io/ServiceWorker/#dom-cache-keys + * @param {any} request + * @param {import('../../types/cache').CacheQueryOptions} options + * @returns {readonly Request[]} + */ + async keys (request = undefined, options = {}) { + webidl.brandCheck(this, Cache) + + if (request !== undefined) request = webidl.converters.RequestInfo(request) + options = webidl.converters.CacheQueryOptions(options) + + // 1. + let r = null + + // 2. + if (request !== undefined) { + // 2.1 + if (request instanceof Request) { + // 2.1.1 + r = request[kState] + + // 2.1.2 + if (r.method !== 'GET' && !options.ignoreMethod) { + return [] + } + } else if (typeof request === 'string') { // 2.2 + r = new Request(request)[kState] + } + } + + // 4. + const promise = createDeferredPromise() + + // 5. + // 5.1 + const requests = [] + + // 5.2 + if (request === undefined) { + // 5.2.1 + for (const requestResponse of this.#relevantRequestResponseList) { + // 5.2.1.1 + requests.push(requestResponse[0]) + } + } else { // 5.3 + // 5.3.1 + const requestResponses = this.#queryCache(r, options) + + // 5.3.2 + for (const requestResponse of requestResponses) { + // 5.3.2.1 + requests.push(requestResponse[0]) + } + } + + // 5.4 + queueMicrotask(() => { + // 5.4.1 + const requestList = [] + + // 5.4.2 + for (const request of requests) { + const requestObject = new Request('https://a') + requestObject[kState] = request + requestObject[kHeaders][kHeadersList] = request.headersList + requestObject[kHeaders][kGuard] = 'immutable' + requestObject[kRealm] = request.client + + // 5.4.2.1 + requestList.push(requestObject) + } + + // 5.4.3 + promise.resolve(Object.freeze(requestList)) + }) + + return promise.promise + } + + /** + * @see https://w3c.github.io/ServiceWorker/#batch-cache-operations-algorithm + * @param {CacheBatchOperation[]} operations + * @returns {requestResponseList} + */ + #batchCacheOperations (operations) { + // 1. + const cache = this.#relevantRequestResponseList + + // 2. + const backupCache = [...cache] + + // 3. + const addedItems = [] + + // 4.1 + const resultList = [] + + try { + // 4.2 + for (const operation of operations) { + // 4.2.1 + if (operation.type !== 'delete' && operation.type !== 'put') { + throw webidl.errors.exception({ + header: 'Cache.#batchCacheOperations', + message: 'operation type does not match "delete" or "put"' + }) + } + + // 4.2.2 + if (operation.type === 'delete' && operation.response != null) { + throw webidl.errors.exception({ + header: 'Cache.#batchCacheOperations', + message: 'delete operation should not have an associated response' + }) + } + + // 4.2.3 + if (this.#queryCache(operation.request, operation.options, addedItems).length) { + throw new DOMException('???', 'InvalidStateError') + } + + // 4.2.4 + let requestResponses + + // 4.2.5 + if (operation.type === 'delete') { + // 4.2.5.1 + requestResponses = this.#queryCache(operation.request, operation.options) + + // TODO: the spec is wrong, this is needed to pass WPTs + if (requestResponses.length === 0) { + return [] + } + + // 4.2.5.2 + for (const requestResponse of requestResponses) { + const idx = cache.indexOf(requestResponse) + assert(idx !== -1) + + // 4.2.5.2.1 + cache.splice(idx, 1) + } + } else if (operation.type === 'put') { // 4.2.6 + // 4.2.6.1 + if (operation.response == null) { + throw webidl.errors.exception({ + header: 'Cache.#batchCacheOperations', + message: 'put operation should have an associated response' + }) + } + + // 4.2.6.2 + const r = operation.request + + // 4.2.6.3 + if (!urlIsHttpHttpsScheme(r.url)) { + throw webidl.errors.exception({ + header: 'Cache.#batchCacheOperations', + message: 'expected http or https scheme' + }) + } + + // 4.2.6.4 + if (r.method !== 'GET') { + throw webidl.errors.exception({ + header: 'Cache.#batchCacheOperations', + message: 'not get method' + }) + } + + // 4.2.6.5 + if (operation.options != null) { + throw webidl.errors.exception({ + header: 'Cache.#batchCacheOperations', + message: 'options must not be defined' + }) + } + + // 4.2.6.6 + requestResponses = this.#queryCache(operation.request) + + // 4.2.6.7 + for (const requestResponse of requestResponses) { + const idx = cache.indexOf(requestResponse) + assert(idx !== -1) + + // 4.2.6.7.1 + cache.splice(idx, 1) + } + + // 4.2.6.8 + cache.push([operation.request, operation.response]) + + // 4.2.6.10 + addedItems.push([operation.request, operation.response]) + } + + // 4.2.7 + resultList.push([operation.request, operation.response]) + } + + // 4.3 + return resultList + } catch (e) { // 5. + // 5.1 + this.#relevantRequestResponseList.length = 0 + + // 5.2 + this.#relevantRequestResponseList = backupCache + + // 5.3 + throw e + } + } + + /** + * @see https://w3c.github.io/ServiceWorker/#query-cache + * @param {any} requestQuery + * @param {import('../../types/cache').CacheQueryOptions} options + * @param {requestResponseList} targetStorage + * @returns {requestResponseList} + */ + #queryCache (requestQuery, options, targetStorage) { + /** @type {requestResponseList} */ + const resultList = [] + + const storage = targetStorage ?? this.#relevantRequestResponseList + + for (const requestResponse of storage) { + const [cachedRequest, cachedResponse] = requestResponse + if (this.#requestMatchesCachedItem(requestQuery, cachedRequest, cachedResponse, options)) { + resultList.push(requestResponse) + } + } + + return resultList + } + + /** + * @see https://w3c.github.io/ServiceWorker/#request-matches-cached-item-algorithm + * @param {any} requestQuery + * @param {any} request + * @param {any | null} response + * @param {import('../../types/cache').CacheQueryOptions | undefined} options + * @returns {boolean} + */ + #requestMatchesCachedItem (requestQuery, request, response = null, options) { + // if (options?.ignoreMethod === false && request.method === 'GET') { + // return false + // } + + const queryURL = new URL(requestQuery.url) + + const cachedURL = new URL(request.url) + + if (options?.ignoreSearch) { + cachedURL.search = '' + + queryURL.search = '' + } + + if (!urlEquals(queryURL, cachedURL, true)) { + return false + } + + if ( + response == null || + options?.ignoreVary || + !response.headersList.contains('vary') + ) { + return true + } + + const fieldValues = getFieldValues(response.headersList.get('vary')) + + for (const fieldValue of fieldValues) { + if (fieldValue === '*') { + return false + } + + const requestValue = request.headersList.get(fieldValue) + const queryValue = requestQuery.headersList.get(fieldValue) + + // If one has the header and the other doesn't, or one has + // a different value than the other, return false + if (requestValue !== queryValue) { + return false + } + } + + return true + } +} + +Object.defineProperties(Cache.prototype, { + [Symbol.toStringTag]: { + value: 'Cache', + configurable: true + }, + match: kEnumerableProperty, + matchAll: kEnumerableProperty, + add: kEnumerableProperty, + addAll: kEnumerableProperty, + put: kEnumerableProperty, + delete: kEnumerableProperty, + keys: kEnumerableProperty +}) + +const cacheQueryOptionConverters = [ + { + key: 'ignoreSearch', + converter: webidl.converters.boolean, + defaultValue: false + }, + { + key: 'ignoreMethod', + converter: webidl.converters.boolean, + defaultValue: false + }, + { + key: 'ignoreVary', + converter: webidl.converters.boolean, + defaultValue: false + } +] + +webidl.converters.CacheQueryOptions = webidl.dictionaryConverter(cacheQueryOptionConverters) + +webidl.converters.MultiCacheQueryOptions = webidl.dictionaryConverter([ + ...cacheQueryOptionConverters, + { + key: 'cacheName', + converter: webidl.converters.DOMString + } +]) + +webidl.converters.Response = webidl.interfaceConverter(Response) + +webidl.converters['sequence'] = webidl.sequenceConverter( + webidl.converters.RequestInfo +) + +module.exports = { + Cache +} + + +/***/ }), + +/***/ 7907: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { kConstruct } = __nccwpck_require__(9174) +const { Cache } = __nccwpck_require__(6101) +const { webidl } = __nccwpck_require__(1744) +const { kEnumerableProperty } = __nccwpck_require__(3983) + +class CacheStorage { + /** + * @see https://w3c.github.io/ServiceWorker/#dfn-relevant-name-to-cache-map + * @type {Map} + */ + async has (cacheName) { + webidl.brandCheck(this, CacheStorage) + webidl.argumentLengthCheck(arguments, 1, { header: 'CacheStorage.has' }) + + cacheName = webidl.converters.DOMString(cacheName) + + // 2.1.1 + // 2.2 + return this.#caches.has(cacheName) + } + + /** + * @see https://w3c.github.io/ServiceWorker/#dom-cachestorage-open + * @param {string} cacheName + * @returns {Promise} + */ + async open (cacheName) { + webidl.brandCheck(this, CacheStorage) + webidl.argumentLengthCheck(arguments, 1, { header: 'CacheStorage.open' }) + + cacheName = webidl.converters.DOMString(cacheName) + + // 2.1 + if (this.#caches.has(cacheName)) { + // await caches.open('v1') !== await caches.open('v1') + + // 2.1.1 + const cache = this.#caches.get(cacheName) + + // 2.1.1.1 + return new Cache(kConstruct, cache) + } + + // 2.2 + const cache = [] + + // 2.3 + this.#caches.set(cacheName, cache) + + // 2.4 + return new Cache(kConstruct, cache) + } + + /** + * @see https://w3c.github.io/ServiceWorker/#cache-storage-delete + * @param {string} cacheName + * @returns {Promise} + */ + async delete (cacheName) { + webidl.brandCheck(this, CacheStorage) + webidl.argumentLengthCheck(arguments, 1, { header: 'CacheStorage.delete' }) + + cacheName = webidl.converters.DOMString(cacheName) + + return this.#caches.delete(cacheName) + } + + /** + * @see https://w3c.github.io/ServiceWorker/#cache-storage-keys + * @returns {string[]} + */ + async keys () { + webidl.brandCheck(this, CacheStorage) + + // 2.1 + const keys = this.#caches.keys() + + // 2.2 + return [...keys] + } +} + +Object.defineProperties(CacheStorage.prototype, { + [Symbol.toStringTag]: { + value: 'CacheStorage', + configurable: true + }, + match: kEnumerableProperty, + has: kEnumerableProperty, + open: kEnumerableProperty, + delete: kEnumerableProperty, + keys: kEnumerableProperty +}) + +module.exports = { + CacheStorage +} + + +/***/ }), + +/***/ 9174: +/***/ ((module) => { + +"use strict"; + + +module.exports = { + kConstruct: Symbol('constructable') +} + + +/***/ }), + +/***/ 2396: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const assert = __nccwpck_require__(9491) +const { URLSerializer } = __nccwpck_require__(685) +const { isValidHeaderName } = __nccwpck_require__(2538) + +/** + * @see https://url.spec.whatwg.org/#concept-url-equals + * @param {URL} A + * @param {URL} B + * @param {boolean | undefined} excludeFragment + * @returns {boolean} + */ +function urlEquals (A, B, excludeFragment = false) { + const serializedA = URLSerializer(A, excludeFragment) + + const serializedB = URLSerializer(B, excludeFragment) + + return serializedA === serializedB +} + +/** + * @see https://github.com/chromium/chromium/blob/694d20d134cb553d8d89e5500b9148012b1ba299/content/browser/cache_storage/cache_storage_cache.cc#L260-L262 + * @param {string} header + */ +function fieldValues (header) { + assert(header !== null) + + const values = [] + + for (let value of header.split(',')) { + value = value.trim() + + if (!value.length) { + continue + } else if (!isValidHeaderName(value)) { + continue + } + + values.push(value) + } + + return values +} + +module.exports = { + urlEquals, + fieldValues +} + + +/***/ }), + +/***/ 3598: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; +// @ts-check + + + +/* global WebAssembly */ + +const assert = __nccwpck_require__(9491) +const net = __nccwpck_require__(1808) +const http = __nccwpck_require__(3685) +const { pipeline } = __nccwpck_require__(2781) +const util = __nccwpck_require__(3983) +const timers = __nccwpck_require__(9459) +const Request = __nccwpck_require__(2905) +const DispatcherBase = __nccwpck_require__(4839) +const { + RequestContentLengthMismatchError, + ResponseContentLengthMismatchError, + InvalidArgumentError, + RequestAbortedError, + HeadersTimeoutError, + HeadersOverflowError, + SocketError, + InformationalError, + BodyTimeoutError, + HTTPParserError, + ResponseExceededMaxSizeError, + ClientDestroyedError +} = __nccwpck_require__(8045) +const buildConnector = __nccwpck_require__(2067) +const { + kUrl, + kReset, + kServerName, + kClient, + kBusy, + kParser, + kConnect, + kBlocking, + kResuming, + kRunning, + kPending, + kSize, + kWriting, + kQueue, + kConnected, + kConnecting, + kNeedDrain, + kNoRef, + kKeepAliveDefaultTimeout, + kHostHeader, + kPendingIdx, + kRunningIdx, + kError, + kPipelining, + kSocket, + kKeepAliveTimeoutValue, + kMaxHeadersSize, + kKeepAliveMaxTimeout, + kKeepAliveTimeoutThreshold, + kHeadersTimeout, + kBodyTimeout, + kStrictContentLength, + kConnector, + kMaxRedirections, + kMaxRequests, + kCounter, + kClose, + kDestroy, + kDispatch, + kInterceptors, + kLocalAddress, + kMaxResponseSize, + kHTTPConnVersion, + // HTTP2 + kHost, + kHTTP2Session, + kHTTP2SessionState, + kHTTP2BuildRequest, + kHTTP2CopyHeaders, + kHTTP1BuildRequest +} = __nccwpck_require__(2785) + +/** @type {import('http2')} */ +let http2 +try { + http2 = __nccwpck_require__(5158) +} catch { + // @ts-ignore + http2 = { constants: {} } +} + +const { + constants: { + HTTP2_HEADER_AUTHORITY, + HTTP2_HEADER_METHOD, + HTTP2_HEADER_PATH, + HTTP2_HEADER_SCHEME, + HTTP2_HEADER_CONTENT_LENGTH, + HTTP2_HEADER_EXPECT, + HTTP2_HEADER_STATUS + } +} = http2 + +// Experimental +let h2ExperimentalWarned = false + +const FastBuffer = Buffer[Symbol.species] + +const kClosedResolve = Symbol('kClosedResolve') + +const channels = {} + +try { + const diagnosticsChannel = __nccwpck_require__(7643) + channels.sendHeaders = diagnosticsChannel.channel('undici:client:sendHeaders') + channels.beforeConnect = diagnosticsChannel.channel('undici:client:beforeConnect') + channels.connectError = diagnosticsChannel.channel('undici:client:connectError') + channels.connected = diagnosticsChannel.channel('undici:client:connected') +} catch { + channels.sendHeaders = { hasSubscribers: false } + channels.beforeConnect = { hasSubscribers: false } + channels.connectError = { hasSubscribers: false } + channels.connected = { hasSubscribers: false } +} + +/** + * @type {import('../types/client').default} + */ +class Client extends DispatcherBase { + /** + * + * @param {string|URL} url + * @param {import('../types/client').Client.Options} options + */ + constructor (url, { + interceptors, + maxHeaderSize, + headersTimeout, + socketTimeout, + requestTimeout, + connectTimeout, + bodyTimeout, + idleTimeout, + keepAlive, + keepAliveTimeout, + maxKeepAliveTimeout, + keepAliveMaxTimeout, + keepAliveTimeoutThreshold, + socketPath, + pipelining, + tls, + strictContentLength, + maxCachedSessions, + maxRedirections, + connect, + maxRequestsPerClient, + localAddress, + maxResponseSize, + autoSelectFamily, + autoSelectFamilyAttemptTimeout, + // h2 + allowH2, + maxConcurrentStreams + } = {}) { + super() + + if (keepAlive !== undefined) { + throw new InvalidArgumentError('unsupported keepAlive, use pipelining=0 instead') + } + + if (socketTimeout !== undefined) { + throw new InvalidArgumentError('unsupported socketTimeout, use headersTimeout & bodyTimeout instead') + } + + if (requestTimeout !== undefined) { + throw new InvalidArgumentError('unsupported requestTimeout, use headersTimeout & bodyTimeout instead') + } + + if (idleTimeout !== undefined) { + throw new InvalidArgumentError('unsupported idleTimeout, use keepAliveTimeout instead') + } + + if (maxKeepAliveTimeout !== undefined) { + throw new InvalidArgumentError('unsupported maxKeepAliveTimeout, use keepAliveMaxTimeout instead') + } + + if (maxHeaderSize != null && !Number.isFinite(maxHeaderSize)) { + throw new InvalidArgumentError('invalid maxHeaderSize') + } + + if (socketPath != null && typeof socketPath !== 'string') { + throw new InvalidArgumentError('invalid socketPath') + } + + if (connectTimeout != null && (!Number.isFinite(connectTimeout) || connectTimeout < 0)) { + throw new InvalidArgumentError('invalid connectTimeout') + } + + if (keepAliveTimeout != null && (!Number.isFinite(keepAliveTimeout) || keepAliveTimeout <= 0)) { + throw new InvalidArgumentError('invalid keepAliveTimeout') + } + + if (keepAliveMaxTimeout != null && (!Number.isFinite(keepAliveMaxTimeout) || keepAliveMaxTimeout <= 0)) { + throw new InvalidArgumentError('invalid keepAliveMaxTimeout') + } + + if (keepAliveTimeoutThreshold != null && !Number.isFinite(keepAliveTimeoutThreshold)) { + throw new InvalidArgumentError('invalid keepAliveTimeoutThreshold') + } + + if (headersTimeout != null && (!Number.isInteger(headersTimeout) || headersTimeout < 0)) { + throw new InvalidArgumentError('headersTimeout must be a positive integer or zero') + } + + if (bodyTimeout != null && (!Number.isInteger(bodyTimeout) || bodyTimeout < 0)) { + throw new InvalidArgumentError('bodyTimeout must be a positive integer or zero') + } + + if (connect != null && typeof connect !== 'function' && typeof connect !== 'object') { + throw new InvalidArgumentError('connect must be a function or an object') + } + + if (maxRedirections != null && (!Number.isInteger(maxRedirections) || maxRedirections < 0)) { + throw new InvalidArgumentError('maxRedirections must be a positive number') + } + + if (maxRequestsPerClient != null && (!Number.isInteger(maxRequestsPerClient) || maxRequestsPerClient < 0)) { + throw new InvalidArgumentError('maxRequestsPerClient must be a positive number') + } + + if (localAddress != null && (typeof localAddress !== 'string' || net.isIP(localAddress) === 0)) { + throw new InvalidArgumentError('localAddress must be valid string IP address') + } + + if (maxResponseSize != null && (!Number.isInteger(maxResponseSize) || maxResponseSize < -1)) { + throw new InvalidArgumentError('maxResponseSize must be a positive number') + } + + if ( + autoSelectFamilyAttemptTimeout != null && + (!Number.isInteger(autoSelectFamilyAttemptTimeout) || autoSelectFamilyAttemptTimeout < -1) + ) { + throw new InvalidArgumentError('autoSelectFamilyAttemptTimeout must be a positive number') + } + + // h2 + if (allowH2 != null && typeof allowH2 !== 'boolean') { + throw new InvalidArgumentError('allowH2 must be a valid boolean value') + } + + if (maxConcurrentStreams != null && (typeof maxConcurrentStreams !== 'number' || maxConcurrentStreams < 1)) { + throw new InvalidArgumentError('maxConcurrentStreams must be a possitive integer, greater than 0') + } + + if (typeof connect !== 'function') { + connect = buildConnector({ + ...tls, + maxCachedSessions, + allowH2, + socketPath, + timeout: connectTimeout, + ...(util.nodeHasAutoSelectFamily && autoSelectFamily ? { autoSelectFamily, autoSelectFamilyAttemptTimeout } : undefined), + ...connect + }) + } + + this[kInterceptors] = interceptors && interceptors.Client && Array.isArray(interceptors.Client) + ? interceptors.Client + : [createRedirectInterceptor({ maxRedirections })] + this[kUrl] = util.parseOrigin(url) + this[kConnector] = connect + this[kSocket] = null + this[kPipelining] = pipelining != null ? pipelining : 1 + this[kMaxHeadersSize] = maxHeaderSize || http.maxHeaderSize + this[kKeepAliveDefaultTimeout] = keepAliveTimeout == null ? 4e3 : keepAliveTimeout + this[kKeepAliveMaxTimeout] = keepAliveMaxTimeout == null ? 600e3 : keepAliveMaxTimeout + this[kKeepAliveTimeoutThreshold] = keepAliveTimeoutThreshold == null ? 1e3 : keepAliveTimeoutThreshold + this[kKeepAliveTimeoutValue] = this[kKeepAliveDefaultTimeout] + this[kServerName] = null + this[kLocalAddress] = localAddress != null ? localAddress : null + this[kResuming] = 0 // 0, idle, 1, scheduled, 2 resuming + this[kNeedDrain] = 0 // 0, idle, 1, scheduled, 2 resuming + this[kHostHeader] = `host: ${this[kUrl].hostname}${this[kUrl].port ? `:${this[kUrl].port}` : ''}\r\n` + this[kBodyTimeout] = bodyTimeout != null ? bodyTimeout : 300e3 + this[kHeadersTimeout] = headersTimeout != null ? headersTimeout : 300e3 + this[kStrictContentLength] = strictContentLength == null ? true : strictContentLength + this[kMaxRedirections] = maxRedirections + this[kMaxRequests] = maxRequestsPerClient + this[kClosedResolve] = null + this[kMaxResponseSize] = maxResponseSize > -1 ? maxResponseSize : -1 + this[kHTTPConnVersion] = 'h1' + + // HTTP/2 + this[kHTTP2Session] = null + this[kHTTP2SessionState] = !allowH2 + ? null + : { + // streams: null, // Fixed queue of streams - For future support of `push` + openStreams: 0, // Keep track of them to decide wether or not unref the session + maxConcurrentStreams: maxConcurrentStreams != null ? maxConcurrentStreams : 100 // Max peerConcurrentStreams for a Node h2 server + } + this[kHost] = `${this[kUrl].hostname}${this[kUrl].port ? `:${this[kUrl].port}` : ''}` + + // kQueue is built up of 3 sections separated by + // the kRunningIdx and kPendingIdx indices. + // | complete | running | pending | + // ^ kRunningIdx ^ kPendingIdx ^ kQueue.length + // kRunningIdx points to the first running element. + // kPendingIdx points to the first pending element. + // This implements a fast queue with an amortized + // time of O(1). + + this[kQueue] = [] + this[kRunningIdx] = 0 + this[kPendingIdx] = 0 + } + + get pipelining () { + return this[kPipelining] + } + + set pipelining (value) { + this[kPipelining] = value + resume(this, true) + } + + get [kPending] () { + return this[kQueue].length - this[kPendingIdx] + } + + get [kRunning] () { + return this[kPendingIdx] - this[kRunningIdx] + } + + get [kSize] () { + return this[kQueue].length - this[kRunningIdx] + } + + get [kConnected] () { + return !!this[kSocket] && !this[kConnecting] && !this[kSocket].destroyed + } + + get [kBusy] () { + const socket = this[kSocket] + return ( + (socket && (socket[kReset] || socket[kWriting] || socket[kBlocking])) || + (this[kSize] >= (this[kPipelining] || 1)) || + this[kPending] > 0 + ) + } + + /* istanbul ignore: only used for test */ + [kConnect] (cb) { + connect(this) + this.once('connect', cb) + } + + [kDispatch] (opts, handler) { + const origin = opts.origin || this[kUrl].origin + + const request = this[kHTTPConnVersion] === 'h2' + ? Request[kHTTP2BuildRequest](origin, opts, handler) + : Request[kHTTP1BuildRequest](origin, opts, handler) + + this[kQueue].push(request) + if (this[kResuming]) { + // Do nothing. + } else if (util.bodyLength(request.body) == null && util.isIterable(request.body)) { + // Wait a tick in case stream/iterator is ended in the same tick. + this[kResuming] = 1 + process.nextTick(resume, this) + } else { + resume(this, true) + } + + if (this[kResuming] && this[kNeedDrain] !== 2 && this[kBusy]) { + this[kNeedDrain] = 2 + } + + return this[kNeedDrain] < 2 + } + + async [kClose] () { + // TODO: for H2 we need to gracefully flush the remaining enqueued + // request and close each stream. + return new Promise((resolve) => { + if (!this[kSize]) { + resolve(null) + } else { + this[kClosedResolve] = resolve + } + }) + } + + async [kDestroy] (err) { + return new Promise((resolve) => { + const requests = this[kQueue].splice(this[kPendingIdx]) + for (let i = 0; i < requests.length; i++) { + const request = requests[i] + errorRequest(this, request, err) + } + + const callback = () => { + if (this[kClosedResolve]) { + // TODO (fix): Should we error here with ClientDestroyedError? + this[kClosedResolve]() + this[kClosedResolve] = null + } + resolve() + } + + if (this[kHTTP2Session] != null) { + util.destroy(this[kHTTP2Session], err) + this[kHTTP2Session] = null + this[kHTTP2SessionState] = null + } + + if (!this[kSocket]) { + queueMicrotask(callback) + } else { + util.destroy(this[kSocket].on('close', callback), err) + } + + resume(this) + }) + } +} + +function onHttp2SessionError (err) { + assert(err.code !== 'ERR_TLS_CERT_ALTNAME_INVALID') + + this[kSocket][kError] = err + + onError(this[kClient], err) +} + +function onHttp2FrameError (type, code, id) { + const err = new InformationalError(`HTTP/2: "frameError" received - type ${type}, code ${code}`) + + if (id === 0) { + this[kSocket][kError] = err + onError(this[kClient], err) + } +} + +function onHttp2SessionEnd () { + util.destroy(this, new SocketError('other side closed')) + util.destroy(this[kSocket], new SocketError('other side closed')) +} + +function onHTTP2GoAway (code) { + const client = this[kClient] + const err = new InformationalError(`HTTP/2: "GOAWAY" frame received with code ${code}`) + client[kSocket] = null + client[kHTTP2Session] = null + + if (client.destroyed) { + assert(this[kPending] === 0) + + // Fail entire queue. + const requests = client[kQueue].splice(client[kRunningIdx]) + for (let i = 0; i < requests.length; i++) { + const request = requests[i] + errorRequest(this, request, err) + } + } else if (client[kRunning] > 0) { + // Fail head of pipeline. + const request = client[kQueue][client[kRunningIdx]] + client[kQueue][client[kRunningIdx]++] = null + + errorRequest(client, request, err) + } + + client[kPendingIdx] = client[kRunningIdx] + + assert(client[kRunning] === 0) + + client.emit('disconnect', + client[kUrl], + [client], + err + ) + + resume(client) +} + +const constants = __nccwpck_require__(953) +const createRedirectInterceptor = __nccwpck_require__(8861) +const EMPTY_BUF = Buffer.alloc(0) + +async function lazyllhttp () { + const llhttpWasmData = process.env.JEST_WORKER_ID ? __nccwpck_require__(1145) : undefined + + let mod + try { + mod = await WebAssembly.compile(Buffer.from(__nccwpck_require__(5627), 'base64')) + } catch (e) { + /* istanbul ignore next */ + + // We could check if the error was caused by the simd option not + // being enabled, but the occurring of this other error + // * https://github.com/emscripten-core/emscripten/issues/11495 + // got me to remove that check to avoid breaking Node 12. + mod = await WebAssembly.compile(Buffer.from(llhttpWasmData || __nccwpck_require__(1145), 'base64')) + } + + return await WebAssembly.instantiate(mod, { + env: { + /* eslint-disable camelcase */ + + wasm_on_url: (p, at, len) => { + /* istanbul ignore next */ + return 0 + }, + wasm_on_status: (p, at, len) => { + assert.strictEqual(currentParser.ptr, p) + const start = at - currentBufferPtr + currentBufferRef.byteOffset + return currentParser.onStatus(new FastBuffer(currentBufferRef.buffer, start, len)) || 0 + }, + wasm_on_message_begin: (p) => { + assert.strictEqual(currentParser.ptr, p) + return currentParser.onMessageBegin() || 0 + }, + wasm_on_header_field: (p, at, len) => { + assert.strictEqual(currentParser.ptr, p) + const start = at - currentBufferPtr + currentBufferRef.byteOffset + return currentParser.onHeaderField(new FastBuffer(currentBufferRef.buffer, start, len)) || 0 + }, + wasm_on_header_value: (p, at, len) => { + assert.strictEqual(currentParser.ptr, p) + const start = at - currentBufferPtr + currentBufferRef.byteOffset + return currentParser.onHeaderValue(new FastBuffer(currentBufferRef.buffer, start, len)) || 0 + }, + wasm_on_headers_complete: (p, statusCode, upgrade, shouldKeepAlive) => { + assert.strictEqual(currentParser.ptr, p) + return currentParser.onHeadersComplete(statusCode, Boolean(upgrade), Boolean(shouldKeepAlive)) || 0 + }, + wasm_on_body: (p, at, len) => { + assert.strictEqual(currentParser.ptr, p) + const start = at - currentBufferPtr + currentBufferRef.byteOffset + return currentParser.onBody(new FastBuffer(currentBufferRef.buffer, start, len)) || 0 + }, + wasm_on_message_complete: (p) => { + assert.strictEqual(currentParser.ptr, p) + return currentParser.onMessageComplete() || 0 + } + + /* eslint-enable camelcase */ + } + }) +} + +let llhttpInstance = null +let llhttpPromise = lazyllhttp() +llhttpPromise.catch() + +let currentParser = null +let currentBufferRef = null +let currentBufferSize = 0 +let currentBufferPtr = null + +const TIMEOUT_HEADERS = 1 +const TIMEOUT_BODY = 2 +const TIMEOUT_IDLE = 3 + +class Parser { + constructor (client, socket, { exports }) { + assert(Number.isFinite(client[kMaxHeadersSize]) && client[kMaxHeadersSize] > 0) + + this.llhttp = exports + this.ptr = this.llhttp.llhttp_alloc(constants.TYPE.RESPONSE) + this.client = client + this.socket = socket + this.timeout = null + this.timeoutValue = null + this.timeoutType = null + this.statusCode = null + this.statusText = '' + this.upgrade = false + this.headers = [] + this.headersSize = 0 + this.headersMaxSize = client[kMaxHeadersSize] + this.shouldKeepAlive = false + this.paused = false + this.resume = this.resume.bind(this) + + this.bytesRead = 0 + + this.keepAlive = '' + this.contentLength = '' + this.connection = '' + this.maxResponseSize = client[kMaxResponseSize] + } + + setTimeout (value, type) { + this.timeoutType = type + if (value !== this.timeoutValue) { + timers.clearTimeout(this.timeout) + if (value) { + this.timeout = timers.setTimeout(onParserTimeout, value, this) + // istanbul ignore else: only for jest + if (this.timeout.unref) { + this.timeout.unref() + } + } else { + this.timeout = null + } + this.timeoutValue = value + } else if (this.timeout) { + // istanbul ignore else: only for jest + if (this.timeout.refresh) { + this.timeout.refresh() + } + } + } + + resume () { + if (this.socket.destroyed || !this.paused) { + return + } + + assert(this.ptr != null) + assert(currentParser == null) + + this.llhttp.llhttp_resume(this.ptr) + + assert(this.timeoutType === TIMEOUT_BODY) + if (this.timeout) { + // istanbul ignore else: only for jest + if (this.timeout.refresh) { + this.timeout.refresh() + } + } + + this.paused = false + this.execute(this.socket.read() || EMPTY_BUF) // Flush parser. + this.readMore() + } + + readMore () { + while (!this.paused && this.ptr) { + const chunk = this.socket.read() + if (chunk === null) { + break + } + this.execute(chunk) + } + } + + execute (data) { + assert(this.ptr != null) + assert(currentParser == null) + assert(!this.paused) + + const { socket, llhttp } = this + + if (data.length > currentBufferSize) { + if (currentBufferPtr) { + llhttp.free(currentBufferPtr) + } + currentBufferSize = Math.ceil(data.length / 4096) * 4096 + currentBufferPtr = llhttp.malloc(currentBufferSize) + } + + new Uint8Array(llhttp.memory.buffer, currentBufferPtr, currentBufferSize).set(data) + + // Call `execute` on the wasm parser. + // We pass the `llhttp_parser` pointer address, the pointer address of buffer view data, + // and finally the length of bytes to parse. + // The return value is an error code or `constants.ERROR.OK`. + try { + let ret + + try { + currentBufferRef = data + currentParser = this + ret = llhttp.llhttp_execute(this.ptr, currentBufferPtr, data.length) + /* eslint-disable-next-line no-useless-catch */ + } catch (err) { + /* istanbul ignore next: difficult to make a test case for */ + throw err + } finally { + currentParser = null + currentBufferRef = null + } + + const offset = llhttp.llhttp_get_error_pos(this.ptr) - currentBufferPtr + + if (ret === constants.ERROR.PAUSED_UPGRADE) { + this.onUpgrade(data.slice(offset)) + } else if (ret === constants.ERROR.PAUSED) { + this.paused = true + socket.unshift(data.slice(offset)) + } else if (ret !== constants.ERROR.OK) { + const ptr = llhttp.llhttp_get_error_reason(this.ptr) + let message = '' + /* istanbul ignore else: difficult to make a test case for */ + if (ptr) { + const len = new Uint8Array(llhttp.memory.buffer, ptr).indexOf(0) + message = + 'Response does not match the HTTP/1.1 protocol (' + + Buffer.from(llhttp.memory.buffer, ptr, len).toString() + + ')' + } + throw new HTTPParserError(message, constants.ERROR[ret], data.slice(offset)) + } + } catch (err) { + util.destroy(socket, err) + } + } + + destroy () { + assert(this.ptr != null) + assert(currentParser == null) + + this.llhttp.llhttp_free(this.ptr) + this.ptr = null + + timers.clearTimeout(this.timeout) + this.timeout = null + this.timeoutValue = null + this.timeoutType = null + + this.paused = false + } + + onStatus (buf) { + this.statusText = buf.toString() + } + + onMessageBegin () { + const { socket, client } = this + + /* istanbul ignore next: difficult to make a test case for */ + if (socket.destroyed) { + return -1 + } + + const request = client[kQueue][client[kRunningIdx]] + if (!request) { + return -1 + } + } + + onHeaderField (buf) { + const len = this.headers.length + + if ((len & 1) === 0) { + this.headers.push(buf) + } else { + this.headers[len - 1] = Buffer.concat([this.headers[len - 1], buf]) + } + + this.trackHeader(buf.length) + } + + onHeaderValue (buf) { + let len = this.headers.length + + if ((len & 1) === 1) { + this.headers.push(buf) + len += 1 + } else { + this.headers[len - 1] = Buffer.concat([this.headers[len - 1], buf]) + } + + const key = this.headers[len - 2] + if (key.length === 10 && key.toString().toLowerCase() === 'keep-alive') { + this.keepAlive += buf.toString() + } else if (key.length === 10 && key.toString().toLowerCase() === 'connection') { + this.connection += buf.toString() + } else if (key.length === 14 && key.toString().toLowerCase() === 'content-length') { + this.contentLength += buf.toString() + } + + this.trackHeader(buf.length) + } + + trackHeader (len) { + this.headersSize += len + if (this.headersSize >= this.headersMaxSize) { + util.destroy(this.socket, new HeadersOverflowError()) + } + } + + onUpgrade (head) { + const { upgrade, client, socket, headers, statusCode } = this + + assert(upgrade) + + const request = client[kQueue][client[kRunningIdx]] + assert(request) + + assert(!socket.destroyed) + assert(socket === client[kSocket]) + assert(!this.paused) + assert(request.upgrade || request.method === 'CONNECT') + + this.statusCode = null + this.statusText = '' + this.shouldKeepAlive = null + + assert(this.headers.length % 2 === 0) + this.headers = [] + this.headersSize = 0 + + socket.unshift(head) + + socket[kParser].destroy() + socket[kParser] = null + + socket[kClient] = null + socket[kError] = null + socket + .removeListener('error', onSocketError) + .removeListener('readable', onSocketReadable) + .removeListener('end', onSocketEnd) + .removeListener('close', onSocketClose) + + client[kSocket] = null + client[kQueue][client[kRunningIdx]++] = null + client.emit('disconnect', client[kUrl], [client], new InformationalError('upgrade')) + + try { + request.onUpgrade(statusCode, headers, socket) + } catch (err) { + util.destroy(socket, err) + } + + resume(client) + } + + onHeadersComplete (statusCode, upgrade, shouldKeepAlive) { + const { client, socket, headers, statusText } = this + + /* istanbul ignore next: difficult to make a test case for */ + if (socket.destroyed) { + return -1 + } + + const request = client[kQueue][client[kRunningIdx]] + + /* istanbul ignore next: difficult to make a test case for */ + if (!request) { + return -1 + } + + assert(!this.upgrade) + assert(this.statusCode < 200) + + if (statusCode === 100) { + util.destroy(socket, new SocketError('bad response', util.getSocketInfo(socket))) + return -1 + } + + /* this can only happen if server is misbehaving */ + if (upgrade && !request.upgrade) { + util.destroy(socket, new SocketError('bad upgrade', util.getSocketInfo(socket))) + return -1 + } + + assert.strictEqual(this.timeoutType, TIMEOUT_HEADERS) + + this.statusCode = statusCode + this.shouldKeepAlive = ( + shouldKeepAlive || + // Override llhttp value which does not allow keepAlive for HEAD. + (request.method === 'HEAD' && !socket[kReset] && this.connection.toLowerCase() === 'keep-alive') + ) + + if (this.statusCode >= 200) { + const bodyTimeout = request.bodyTimeout != null + ? request.bodyTimeout + : client[kBodyTimeout] + this.setTimeout(bodyTimeout, TIMEOUT_BODY) + } else if (this.timeout) { + // istanbul ignore else: only for jest + if (this.timeout.refresh) { + this.timeout.refresh() + } + } + + if (request.method === 'CONNECT') { + assert(client[kRunning] === 1) + this.upgrade = true + return 2 + } + + if (upgrade) { + assert(client[kRunning] === 1) + this.upgrade = true + return 2 + } + + assert(this.headers.length % 2 === 0) + this.headers = [] + this.headersSize = 0 + + if (this.shouldKeepAlive && client[kPipelining]) { + const keepAliveTimeout = this.keepAlive ? util.parseKeepAliveTimeout(this.keepAlive) : null + + if (keepAliveTimeout != null) { + const timeout = Math.min( + keepAliveTimeout - client[kKeepAliveTimeoutThreshold], + client[kKeepAliveMaxTimeout] + ) + if (timeout <= 0) { + socket[kReset] = true + } else { + client[kKeepAliveTimeoutValue] = timeout + } + } else { + client[kKeepAliveTimeoutValue] = client[kKeepAliveDefaultTimeout] + } + } else { + // Stop more requests from being dispatched. + socket[kReset] = true + } + + let pause + try { + pause = request.onHeaders(statusCode, headers, this.resume, statusText) === false + } catch (err) { + util.destroy(socket, err) + return -1 + } + + if (request.method === 'HEAD') { + return 1 + } + + if (statusCode < 200) { + return 1 + } + + if (socket[kBlocking]) { + socket[kBlocking] = false + resume(client) + } + + return pause ? constants.ERROR.PAUSED : 0 + } + + onBody (buf) { + const { client, socket, statusCode, maxResponseSize } = this + + if (socket.destroyed) { + return -1 + } + + const request = client[kQueue][client[kRunningIdx]] + assert(request) + + assert.strictEqual(this.timeoutType, TIMEOUT_BODY) + if (this.timeout) { + // istanbul ignore else: only for jest + if (this.timeout.refresh) { + this.timeout.refresh() + } + } + + assert(statusCode >= 200) + + if (maxResponseSize > -1 && this.bytesRead + buf.length > maxResponseSize) { + util.destroy(socket, new ResponseExceededMaxSizeError()) + return -1 + } + + this.bytesRead += buf.length + + try { + if (request.onData(buf) === false) { + return constants.ERROR.PAUSED + } + } catch (err) { + util.destroy(socket, err) + return -1 + } + } + + onMessageComplete () { + const { client, socket, statusCode, upgrade, headers, contentLength, bytesRead, shouldKeepAlive } = this + + if (socket.destroyed && (!statusCode || shouldKeepAlive)) { + return -1 + } + + if (upgrade) { + return + } + + const request = client[kQueue][client[kRunningIdx]] + assert(request) + + assert(statusCode >= 100) + + this.statusCode = null + this.statusText = '' + this.bytesRead = 0 + this.contentLength = '' + this.keepAlive = '' + this.connection = '' + + assert(this.headers.length % 2 === 0) + this.headers = [] + this.headersSize = 0 + + if (statusCode < 200) { + return + } + + /* istanbul ignore next: should be handled by llhttp? */ + if (request.method !== 'HEAD' && contentLength && bytesRead !== parseInt(contentLength, 10)) { + util.destroy(socket, new ResponseContentLengthMismatchError()) + return -1 + } + + try { + request.onComplete(headers) + } catch (err) { + errorRequest(client, request, err) + } + + client[kQueue][client[kRunningIdx]++] = null + + if (socket[kWriting]) { + assert.strictEqual(client[kRunning], 0) + // Response completed before request. + util.destroy(socket, new InformationalError('reset')) + return constants.ERROR.PAUSED + } else if (!shouldKeepAlive) { + util.destroy(socket, new InformationalError('reset')) + return constants.ERROR.PAUSED + } else if (socket[kReset] && client[kRunning] === 0) { + // Destroy socket once all requests have completed. + // The request at the tail of the pipeline is the one + // that requested reset and no further requests should + // have been queued since then. + util.destroy(socket, new InformationalError('reset')) + return constants.ERROR.PAUSED + } else if (client[kPipelining] === 1) { + // We must wait a full event loop cycle to reuse this socket to make sure + // that non-spec compliant servers are not closing the connection even if they + // said they won't. + setImmediate(resume, client) + } else { + resume(client) + } + } +} + +function onParserTimeout (parser) { + const { socket, timeoutType, client } = parser + + /* istanbul ignore else */ + if (timeoutType === TIMEOUT_HEADERS) { + if (!socket[kWriting] || socket.writableNeedDrain || client[kRunning] > 1) { + assert(!parser.paused, 'cannot be paused while waiting for headers') + util.destroy(socket, new HeadersTimeoutError()) + } + } else if (timeoutType === TIMEOUT_BODY) { + if (!parser.paused) { + util.destroy(socket, new BodyTimeoutError()) + } + } else if (timeoutType === TIMEOUT_IDLE) { + assert(client[kRunning] === 0 && client[kKeepAliveTimeoutValue]) + util.destroy(socket, new InformationalError('socket idle timeout')) + } +} + +function onSocketReadable () { + const { [kParser]: parser } = this + if (parser) { + parser.readMore() + } +} + +function onSocketError (err) { + const { [kClient]: client, [kParser]: parser } = this + + assert(err.code !== 'ERR_TLS_CERT_ALTNAME_INVALID') + + if (client[kHTTPConnVersion] !== 'h2') { + // On Mac OS, we get an ECONNRESET even if there is a full body to be forwarded + // to the user. + if (err.code === 'ECONNRESET' && parser.statusCode && !parser.shouldKeepAlive) { + // We treat all incoming data so for as a valid response. + parser.onMessageComplete() + return + } + } + + this[kError] = err + + onError(this[kClient], err) +} + +function onError (client, err) { + if ( + client[kRunning] === 0 && + err.code !== 'UND_ERR_INFO' && + err.code !== 'UND_ERR_SOCKET' + ) { + // Error is not caused by running request and not a recoverable + // socket error. + + assert(client[kPendingIdx] === client[kRunningIdx]) + + const requests = client[kQueue].splice(client[kRunningIdx]) + for (let i = 0; i < requests.length; i++) { + const request = requests[i] + errorRequest(client, request, err) + } + assert(client[kSize] === 0) + } +} + +function onSocketEnd () { + const { [kParser]: parser, [kClient]: client } = this + + if (client[kHTTPConnVersion] !== 'h2') { + if (parser.statusCode && !parser.shouldKeepAlive) { + // We treat all incoming data so far as a valid response. + parser.onMessageComplete() + return + } + } + + util.destroy(this, new SocketError('other side closed', util.getSocketInfo(this))) +} + +function onSocketClose () { + const { [kClient]: client, [kParser]: parser } = this + + if (client[kHTTPConnVersion] === 'h1' && parser) { + if (!this[kError] && parser.statusCode && !parser.shouldKeepAlive) { + // We treat all incoming data so far as a valid response. + parser.onMessageComplete() + } + + this[kParser].destroy() + this[kParser] = null + } + + const err = this[kError] || new SocketError('closed', util.getSocketInfo(this)) + + client[kSocket] = null + + if (client.destroyed) { + assert(client[kPending] === 0) + + // Fail entire queue. + const requests = client[kQueue].splice(client[kRunningIdx]) + for (let i = 0; i < requests.length; i++) { + const request = requests[i] + errorRequest(client, request, err) + } + } else if (client[kRunning] > 0 && err.code !== 'UND_ERR_INFO') { + // Fail head of pipeline. + const request = client[kQueue][client[kRunningIdx]] + client[kQueue][client[kRunningIdx]++] = null + + errorRequest(client, request, err) + } + + client[kPendingIdx] = client[kRunningIdx] + + assert(client[kRunning] === 0) + + client.emit('disconnect', client[kUrl], [client], err) + + resume(client) +} + +async function connect (client) { + assert(!client[kConnecting]) + assert(!client[kSocket]) + + let { host, hostname, protocol, port } = client[kUrl] + + // Resolve ipv6 + if (hostname[0] === '[') { + const idx = hostname.indexOf(']') + + assert(idx !== -1) + const ip = hostname.substr(1, idx - 1) + + assert(net.isIP(ip)) + hostname = ip + } + + client[kConnecting] = true + + if (channels.beforeConnect.hasSubscribers) { + channels.beforeConnect.publish({ + connectParams: { + host, + hostname, + protocol, + port, + servername: client[kServerName], + localAddress: client[kLocalAddress] + }, + connector: client[kConnector] + }) + } + + try { + const socket = await new Promise((resolve, reject) => { + client[kConnector]({ + host, + hostname, + protocol, + port, + servername: client[kServerName], + localAddress: client[kLocalAddress] + }, (err, socket) => { + if (err) { + reject(err) + } else { + resolve(socket) + } + }) + }) + + if (client.destroyed) { + util.destroy(socket.on('error', () => {}), new ClientDestroyedError()) + return + } + + client[kConnecting] = false + + assert(socket) + + const isH2 = socket.alpnProtocol === 'h2' + if (isH2) { + if (!h2ExperimentalWarned) { + h2ExperimentalWarned = true + process.emitWarning('H2 support is experimental, expect them to change at any time.', { + code: 'UNDICI-H2' + }) + } + + const session = http2.connect(client[kUrl], { + createConnection: () => socket, + peerMaxConcurrentStreams: client[kHTTP2SessionState].maxConcurrentStreams + }) + + client[kHTTPConnVersion] = 'h2' + session[kClient] = client + session[kSocket] = socket + session.on('error', onHttp2SessionError) + session.on('frameError', onHttp2FrameError) + session.on('end', onHttp2SessionEnd) + session.on('goaway', onHTTP2GoAway) + session.on('close', onSocketClose) + session.unref() + + client[kHTTP2Session] = session + socket[kHTTP2Session] = session + } else { + if (!llhttpInstance) { + llhttpInstance = await llhttpPromise + llhttpPromise = null + } + + socket[kNoRef] = false + socket[kWriting] = false + socket[kReset] = false + socket[kBlocking] = false + socket[kParser] = new Parser(client, socket, llhttpInstance) + } + + socket[kCounter] = 0 + socket[kMaxRequests] = client[kMaxRequests] + socket[kClient] = client + socket[kError] = null + + socket + .on('error', onSocketError) + .on('readable', onSocketReadable) + .on('end', onSocketEnd) + .on('close', onSocketClose) + + client[kSocket] = socket + + if (channels.connected.hasSubscribers) { + channels.connected.publish({ + connectParams: { + host, + hostname, + protocol, + port, + servername: client[kServerName], + localAddress: client[kLocalAddress] + }, + connector: client[kConnector], + socket + }) + } + client.emit('connect', client[kUrl], [client]) + } catch (err) { + if (client.destroyed) { + return + } + + client[kConnecting] = false + + if (channels.connectError.hasSubscribers) { + channels.connectError.publish({ + connectParams: { + host, + hostname, + protocol, + port, + servername: client[kServerName], + localAddress: client[kLocalAddress] + }, + connector: client[kConnector], + error: err + }) + } + + if (err.code === 'ERR_TLS_CERT_ALTNAME_INVALID') { + assert(client[kRunning] === 0) + while (client[kPending] > 0 && client[kQueue][client[kPendingIdx]].servername === client[kServerName]) { + const request = client[kQueue][client[kPendingIdx]++] + errorRequest(client, request, err) + } + } else { + onError(client, err) + } + + client.emit('connectionError', client[kUrl], [client], err) + } + + resume(client) +} + +function emitDrain (client) { + client[kNeedDrain] = 0 + client.emit('drain', client[kUrl], [client]) +} + +function resume (client, sync) { + if (client[kResuming] === 2) { + return + } + + client[kResuming] = 2 + + _resume(client, sync) + client[kResuming] = 0 + + if (client[kRunningIdx] > 256) { + client[kQueue].splice(0, client[kRunningIdx]) + client[kPendingIdx] -= client[kRunningIdx] + client[kRunningIdx] = 0 + } +} + +function _resume (client, sync) { + while (true) { + if (client.destroyed) { + assert(client[kPending] === 0) + return + } + + if (client[kClosedResolve] && !client[kSize]) { + client[kClosedResolve]() + client[kClosedResolve] = null + return + } + + const socket = client[kSocket] + + if (socket && !socket.destroyed && socket.alpnProtocol !== 'h2') { + if (client[kSize] === 0) { + if (!socket[kNoRef] && socket.unref) { + socket.unref() + socket[kNoRef] = true + } + } else if (socket[kNoRef] && socket.ref) { + socket.ref() + socket[kNoRef] = false + } + + if (client[kSize] === 0) { + if (socket[kParser].timeoutType !== TIMEOUT_IDLE) { + socket[kParser].setTimeout(client[kKeepAliveTimeoutValue], TIMEOUT_IDLE) + } + } else if (client[kRunning] > 0 && socket[kParser].statusCode < 200) { + if (socket[kParser].timeoutType !== TIMEOUT_HEADERS) { + const request = client[kQueue][client[kRunningIdx]] + const headersTimeout = request.headersTimeout != null + ? request.headersTimeout + : client[kHeadersTimeout] + socket[kParser].setTimeout(headersTimeout, TIMEOUT_HEADERS) + } + } + } + + if (client[kBusy]) { + client[kNeedDrain] = 2 + } else if (client[kNeedDrain] === 2) { + if (sync) { + client[kNeedDrain] = 1 + process.nextTick(emitDrain, client) + } else { + emitDrain(client) + } + continue + } + + if (client[kPending] === 0) { + return + } + + if (client[kRunning] >= (client[kPipelining] || 1)) { + return + } + + const request = client[kQueue][client[kPendingIdx]] + + if (client[kUrl].protocol === 'https:' && client[kServerName] !== request.servername) { + if (client[kRunning] > 0) { + return + } + + client[kServerName] = request.servername + + if (socket && socket.servername !== request.servername) { + util.destroy(socket, new InformationalError('servername changed')) + return + } + } + + if (client[kConnecting]) { + return + } + + if (!socket && !client[kHTTP2Session]) { + connect(client) + return + } + + if (socket.destroyed || socket[kWriting] || socket[kReset] || socket[kBlocking]) { + return + } + + if (client[kRunning] > 0 && !request.idempotent) { + // Non-idempotent request cannot be retried. + // Ensure that no other requests are inflight and + // could cause failure. + return + } + + if (client[kRunning] > 0 && (request.upgrade || request.method === 'CONNECT')) { + // Don't dispatch an upgrade until all preceding requests have completed. + // A misbehaving server might upgrade the connection before all pipelined + // request has completed. + return + } + + if (client[kRunning] > 0 && util.bodyLength(request.body) !== 0 && + (util.isStream(request.body) || util.isAsyncIterable(request.body))) { + // Request with stream or iterator body can error while other requests + // are inflight and indirectly error those as well. + // Ensure this doesn't happen by waiting for inflight + // to complete before dispatching. + + // Request with stream or iterator body cannot be retried. + // Ensure that no other requests are inflight and + // could cause failure. + return + } + + if (!request.aborted && write(client, request)) { + client[kPendingIdx]++ + } else { + client[kQueue].splice(client[kPendingIdx], 1) + } + } +} + +// https://www.rfc-editor.org/rfc/rfc7230#section-3.3.2 +function shouldSendContentLength (method) { + return method !== 'GET' && method !== 'HEAD' && method !== 'OPTIONS' && method !== 'TRACE' && method !== 'CONNECT' +} + +function write (client, request) { + if (client[kHTTPConnVersion] === 'h2') { + writeH2(client, client[kHTTP2Session], request) + return + } + + const { body, method, path, host, upgrade, headers, blocking, reset } = request + + // https://tools.ietf.org/html/rfc7231#section-4.3.1 + // https://tools.ietf.org/html/rfc7231#section-4.3.2 + // https://tools.ietf.org/html/rfc7231#section-4.3.5 + + // Sending a payload body on a request that does not + // expect it can cause undefined behavior on some + // servers and corrupt connection state. Do not + // re-use the connection for further requests. + + const expectsPayload = ( + method === 'PUT' || + method === 'POST' || + method === 'PATCH' + ) + + if (body && typeof body.read === 'function') { + // Try to read EOF in order to get length. + body.read(0) + } + + const bodyLength = util.bodyLength(body) + + let contentLength = bodyLength + + if (contentLength === null) { + contentLength = request.contentLength + } + + if (contentLength === 0 && !expectsPayload) { + // https://tools.ietf.org/html/rfc7230#section-3.3.2 + // A user agent SHOULD NOT send a Content-Length header field when + // the request message does not contain a payload body and the method + // semantics do not anticipate such a body. + + contentLength = null + } + + // https://github.com/nodejs/undici/issues/2046 + // A user agent may send a Content-Length header with 0 value, this should be allowed. + if (shouldSendContentLength(method) && contentLength > 0 && request.contentLength !== null && request.contentLength !== contentLength) { + if (client[kStrictContentLength]) { + errorRequest(client, request, new RequestContentLengthMismatchError()) + return false + } + + process.emitWarning(new RequestContentLengthMismatchError()) + } + + const socket = client[kSocket] + + try { + request.onConnect((err) => { + if (request.aborted || request.completed) { + return + } + + errorRequest(client, request, err || new RequestAbortedError()) + + util.destroy(socket, new InformationalError('aborted')) + }) + } catch (err) { + errorRequest(client, request, err) + } + + if (request.aborted) { + return false + } + + if (method === 'HEAD') { + // https://github.com/mcollina/undici/issues/258 + // Close after a HEAD request to interop with misbehaving servers + // that may send a body in the response. + + socket[kReset] = true + } + + if (upgrade || method === 'CONNECT') { + // On CONNECT or upgrade, block pipeline from dispatching further + // requests on this connection. + + socket[kReset] = true + } + + if (reset != null) { + socket[kReset] = reset + } + + if (client[kMaxRequests] && socket[kCounter]++ >= client[kMaxRequests]) { + socket[kReset] = true + } + + if (blocking) { + socket[kBlocking] = true + } + + let header = `${method} ${path} HTTP/1.1\r\n` + + if (typeof host === 'string') { + header += `host: ${host}\r\n` + } else { + header += client[kHostHeader] + } + + if (upgrade) { + header += `connection: upgrade\r\nupgrade: ${upgrade}\r\n` + } else if (client[kPipelining] && !socket[kReset]) { + header += 'connection: keep-alive\r\n' + } else { + header += 'connection: close\r\n' + } + + if (headers) { + header += headers + } + + if (channels.sendHeaders.hasSubscribers) { + channels.sendHeaders.publish({ request, headers: header, socket }) + } + + /* istanbul ignore else: assertion */ + if (!body || bodyLength === 0) { + if (contentLength === 0) { + socket.write(`${header}content-length: 0\r\n\r\n`, 'latin1') + } else { + assert(contentLength === null, 'no body must not have content length') + socket.write(`${header}\r\n`, 'latin1') + } + request.onRequestSent() + } else if (util.isBuffer(body)) { + assert(contentLength === body.byteLength, 'buffer body must have content length') + + socket.cork() + socket.write(`${header}content-length: ${contentLength}\r\n\r\n`, 'latin1') + socket.write(body) + socket.uncork() + request.onBodySent(body) + request.onRequestSent() + if (!expectsPayload) { + socket[kReset] = true + } + } else if (util.isBlobLike(body)) { + if (typeof body.stream === 'function') { + writeIterable({ body: body.stream(), client, request, socket, contentLength, header, expectsPayload }) + } else { + writeBlob({ body, client, request, socket, contentLength, header, expectsPayload }) + } + } else if (util.isStream(body)) { + writeStream({ body, client, request, socket, contentLength, header, expectsPayload }) + } else if (util.isIterable(body)) { + writeIterable({ body, client, request, socket, contentLength, header, expectsPayload }) + } else { + assert(false) + } + + return true +} + +function writeH2 (client, session, request) { + const { body, method, path, host, upgrade, expectContinue, signal, headers: reqHeaders } = request + + let headers + if (typeof reqHeaders === 'string') headers = Request[kHTTP2CopyHeaders](reqHeaders.trim()) + else headers = reqHeaders + + if (upgrade) { + errorRequest(client, request, new Error('Upgrade not supported for H2')) + return false + } + + try { + // TODO(HTTP/2): Should we call onConnect immediately or on stream ready event? + request.onConnect((err) => { + if (request.aborted || request.completed) { + return + } + + errorRequest(client, request, err || new RequestAbortedError()) + }) + } catch (err) { + errorRequest(client, request, err) + } + + if (request.aborted) { + return false + } + + let stream + const h2State = client[kHTTP2SessionState] + + headers[HTTP2_HEADER_AUTHORITY] = host || client[kHost] + headers[HTTP2_HEADER_METHOD] = method + + if (method === 'CONNECT') { + session.ref() + // we are already connected, streams are pending, first request + // will create a new stream. We trigger a request to create the stream and wait until + // `ready` event is triggered + // We disabled endStream to allow the user to write to the stream + stream = session.request(headers, { endStream: false, signal }) + + if (stream.id && !stream.pending) { + request.onUpgrade(null, null, stream) + ++h2State.openStreams + } else { + stream.once('ready', () => { + request.onUpgrade(null, null, stream) + ++h2State.openStreams + }) + } + + stream.once('close', () => { + h2State.openStreams -= 1 + // TODO(HTTP/2): unref only if current streams count is 0 + if (h2State.openStreams === 0) session.unref() + }) + + return true + } + + // https://tools.ietf.org/html/rfc7540#section-8.3 + // :path and :scheme headers must be omited when sending CONNECT + + headers[HTTP2_HEADER_PATH] = path + headers[HTTP2_HEADER_SCHEME] = 'https' + + // https://tools.ietf.org/html/rfc7231#section-4.3.1 + // https://tools.ietf.org/html/rfc7231#section-4.3.2 + // https://tools.ietf.org/html/rfc7231#section-4.3.5 + + // Sending a payload body on a request that does not + // expect it can cause undefined behavior on some + // servers and corrupt connection state. Do not + // re-use the connection for further requests. + + const expectsPayload = ( + method === 'PUT' || + method === 'POST' || + method === 'PATCH' + ) + + if (body && typeof body.read === 'function') { + // Try to read EOF in order to get length. + body.read(0) + } + + let contentLength = util.bodyLength(body) + + if (contentLength == null) { + contentLength = request.contentLength + } + + if (contentLength === 0 || !expectsPayload) { + // https://tools.ietf.org/html/rfc7230#section-3.3.2 + // A user agent SHOULD NOT send a Content-Length header field when + // the request message does not contain a payload body and the method + // semantics do not anticipate such a body. + + contentLength = null + } + + // https://github.com/nodejs/undici/issues/2046 + // A user agent may send a Content-Length header with 0 value, this should be allowed. + if (shouldSendContentLength(method) && contentLength > 0 && request.contentLength != null && request.contentLength !== contentLength) { + if (client[kStrictContentLength]) { + errorRequest(client, request, new RequestContentLengthMismatchError()) + return false + } + + process.emitWarning(new RequestContentLengthMismatchError()) + } + + if (contentLength != null) { + assert(body, 'no body must not have content length') + headers[HTTP2_HEADER_CONTENT_LENGTH] = `${contentLength}` + } + + session.ref() + + const shouldEndStream = method === 'GET' || method === 'HEAD' + if (expectContinue) { + headers[HTTP2_HEADER_EXPECT] = '100-continue' + /** + * @type {import('node:http2').ClientHttp2Stream} + */ + stream = session.request(headers, { endStream: shouldEndStream, signal }) + + stream.once('continue', writeBodyH2) + } else { + /** @type {import('node:http2').ClientHttp2Stream} */ + stream = session.request(headers, { + endStream: shouldEndStream, + signal + }) + writeBodyH2() + } + + // Increment counter as we have new several streams open + ++h2State.openStreams + + stream.once('response', headers => { + if (request.onHeaders(Number(headers[HTTP2_HEADER_STATUS]), headers, stream.resume.bind(stream), '') === false) { + stream.pause() + } + }) + + stream.once('end', () => { + request.onComplete([]) + }) + + stream.on('data', (chunk) => { + if (request.onData(chunk) === false) stream.pause() + }) + + stream.once('close', () => { + h2State.openStreams -= 1 + // TODO(HTTP/2): unref only if current streams count is 0 + if (h2State.openStreams === 0) session.unref() + }) + + stream.once('error', function (err) { + if (client[kHTTP2Session] && !client[kHTTP2Session].destroyed && !this.closed && !this.destroyed) { + h2State.streams -= 1 + util.destroy(stream, err) + } + }) + + stream.once('frameError', (type, code) => { + const err = new InformationalError(`HTTP/2: "frameError" received - type ${type}, code ${code}`) + errorRequest(client, request, err) + + if (client[kHTTP2Session] && !client[kHTTP2Session].destroyed && !this.closed && !this.destroyed) { + h2State.streams -= 1 + util.destroy(stream, err) + } + }) + + // stream.on('aborted', () => { + // // TODO(HTTP/2): Support aborted + // }) + + // stream.on('timeout', () => { + // // TODO(HTTP/2): Support timeout + // }) + + // stream.on('push', headers => { + // // TODO(HTTP/2): Suppor push + // }) + + // stream.on('trailers', headers => { + // // TODO(HTTP/2): Support trailers + // }) + + return true + + function writeBodyH2 () { + /* istanbul ignore else: assertion */ + if (!body) { + request.onRequestSent() + } else if (util.isBuffer(body)) { + assert(contentLength === body.byteLength, 'buffer body must have content length') + stream.cork() + stream.write(body) + stream.uncork() + stream.end() + request.onBodySent(body) + request.onRequestSent() + } else if (util.isBlobLike(body)) { + if (typeof body.stream === 'function') { + writeIterable({ + client, + request, + contentLength, + h2stream: stream, + expectsPayload, + body: body.stream(), + socket: client[kSocket], + header: '' + }) + } else { + writeBlob({ + body, + client, + request, + contentLength, + expectsPayload, + h2stream: stream, + header: '', + socket: client[kSocket] + }) + } + } else if (util.isStream(body)) { + writeStream({ + body, + client, + request, + contentLength, + expectsPayload, + socket: client[kSocket], + h2stream: stream, + header: '' + }) + } else if (util.isIterable(body)) { + writeIterable({ + body, + client, + request, + contentLength, + expectsPayload, + header: '', + h2stream: stream, + socket: client[kSocket] + }) + } else { + assert(false) + } + } +} + +function writeStream ({ h2stream, body, client, request, socket, contentLength, header, expectsPayload }) { + assert(contentLength !== 0 || client[kRunning] === 0, 'stream body cannot be pipelined') + + if (client[kHTTPConnVersion] === 'h2') { + // For HTTP/2, is enough to pipe the stream + const pipe = pipeline( + body, + h2stream, + (err) => { + if (err) { + util.destroy(body, err) + util.destroy(h2stream, err) + } else { + request.onRequestSent() + } + } + ) + + pipe.on('data', onPipeData) + pipe.once('end', () => { + pipe.removeListener('data', onPipeData) + util.destroy(pipe) + }) + + function onPipeData (chunk) { + request.onBodySent(chunk) + } + + return + } + + let finished = false + + const writer = new AsyncWriter({ socket, request, contentLength, client, expectsPayload, header }) + + const onData = function (chunk) { + if (finished) { + return + } + + try { + if (!writer.write(chunk) && this.pause) { + this.pause() + } + } catch (err) { + util.destroy(this, err) + } + } + const onDrain = function () { + if (finished) { + return + } + + if (body.resume) { + body.resume() + } + } + const onAbort = function () { + onFinished(new RequestAbortedError()) + } + const onFinished = function (err) { + if (finished) { + return + } + + finished = true + + assert(socket.destroyed || (socket[kWriting] && client[kRunning] <= 1)) + + socket + .off('drain', onDrain) + .off('error', onFinished) + + body + .removeListener('data', onData) + .removeListener('end', onFinished) + .removeListener('error', onFinished) + .removeListener('close', onAbort) + + if (!err) { + try { + writer.end() + } catch (er) { + err = er + } + } + + writer.destroy(err) + + if (err && (err.code !== 'UND_ERR_INFO' || err.message !== 'reset')) { + util.destroy(body, err) + } else { + util.destroy(body) + } + } + + body + .on('data', onData) + .on('end', onFinished) + .on('error', onFinished) + .on('close', onAbort) + + if (body.resume) { + body.resume() + } + + socket + .on('drain', onDrain) + .on('error', onFinished) +} + +async function writeBlob ({ h2stream, body, client, request, socket, contentLength, header, expectsPayload }) { + assert(contentLength === body.size, 'blob body must have content length') + + const isH2 = client[kHTTPConnVersion] === 'h2' + try { + if (contentLength != null && contentLength !== body.size) { + throw new RequestContentLengthMismatchError() + } + + const buffer = Buffer.from(await body.arrayBuffer()) + + if (isH2) { + h2stream.cork() + h2stream.write(buffer) + h2stream.uncork() + } else { + socket.cork() + socket.write(`${header}content-length: ${contentLength}\r\n\r\n`, 'latin1') + socket.write(buffer) + socket.uncork() + } + + request.onBodySent(buffer) + request.onRequestSent() + + if (!expectsPayload) { + socket[kReset] = true + } + + resume(client) + } catch (err) { + util.destroy(isH2 ? h2stream : socket, err) + } +} + +async function writeIterable ({ h2stream, body, client, request, socket, contentLength, header, expectsPayload }) { + assert(contentLength !== 0 || client[kRunning] === 0, 'iterator body cannot be pipelined') + + let callback = null + function onDrain () { + if (callback) { + const cb = callback + callback = null + cb() + } + } + + const waitForDrain = () => new Promise((resolve, reject) => { + assert(callback === null) + + if (socket[kError]) { + reject(socket[kError]) + } else { + callback = resolve + } + }) + + if (client[kHTTPConnVersion] === 'h2') { + h2stream + .on('close', onDrain) + .on('drain', onDrain) + + try { + // It's up to the user to somehow abort the async iterable. + for await (const chunk of body) { + if (socket[kError]) { + throw socket[kError] + } + + const res = h2stream.write(chunk) + request.onBodySent(chunk) + if (!res) { + await waitForDrain() + } + } + } catch (err) { + h2stream.destroy(err) + } finally { + request.onRequestSent() + h2stream.end() + h2stream + .off('close', onDrain) + .off('drain', onDrain) + } + + return + } + + socket + .on('close', onDrain) + .on('drain', onDrain) + + const writer = new AsyncWriter({ socket, request, contentLength, client, expectsPayload, header }) + try { + // It's up to the user to somehow abort the async iterable. + for await (const chunk of body) { + if (socket[kError]) { + throw socket[kError] + } + + if (!writer.write(chunk)) { + await waitForDrain() + } + } + + writer.end() + } catch (err) { + writer.destroy(err) + } finally { + socket + .off('close', onDrain) + .off('drain', onDrain) + } +} + +class AsyncWriter { + constructor ({ socket, request, contentLength, client, expectsPayload, header }) { + this.socket = socket + this.request = request + this.contentLength = contentLength + this.client = client + this.bytesWritten = 0 + this.expectsPayload = expectsPayload + this.header = header + + socket[kWriting] = true + } + + write (chunk) { + const { socket, request, contentLength, client, bytesWritten, expectsPayload, header } = this + + if (socket[kError]) { + throw socket[kError] + } + + if (socket.destroyed) { + return false + } + + const len = Buffer.byteLength(chunk) + if (!len) { + return true + } + + // We should defer writing chunks. + if (contentLength !== null && bytesWritten + len > contentLength) { + if (client[kStrictContentLength]) { + throw new RequestContentLengthMismatchError() + } + + process.emitWarning(new RequestContentLengthMismatchError()) + } + + socket.cork() + + if (bytesWritten === 0) { + if (!expectsPayload) { + socket[kReset] = true + } + + if (contentLength === null) { + socket.write(`${header}transfer-encoding: chunked\r\n`, 'latin1') + } else { + socket.write(`${header}content-length: ${contentLength}\r\n\r\n`, 'latin1') + } + } + + if (contentLength === null) { + socket.write(`\r\n${len.toString(16)}\r\n`, 'latin1') + } + + this.bytesWritten += len + + const ret = socket.write(chunk) + + socket.uncork() + + request.onBodySent(chunk) + + if (!ret) { + if (socket[kParser].timeout && socket[kParser].timeoutType === TIMEOUT_HEADERS) { + // istanbul ignore else: only for jest + if (socket[kParser].timeout.refresh) { + socket[kParser].timeout.refresh() + } + } + } + + return ret + } + + end () { + const { socket, contentLength, client, bytesWritten, expectsPayload, header, request } = this + request.onRequestSent() + + socket[kWriting] = false + + if (socket[kError]) { + throw socket[kError] + } + + if (socket.destroyed) { + return + } + + if (bytesWritten === 0) { + if (expectsPayload) { + // https://tools.ietf.org/html/rfc7230#section-3.3.2 + // A user agent SHOULD send a Content-Length in a request message when + // no Transfer-Encoding is sent and the request method defines a meaning + // for an enclosed payload body. + + socket.write(`${header}content-length: 0\r\n\r\n`, 'latin1') + } else { + socket.write(`${header}\r\n`, 'latin1') + } + } else if (contentLength === null) { + socket.write('\r\n0\r\n\r\n', 'latin1') + } + + if (contentLength !== null && bytesWritten !== contentLength) { + if (client[kStrictContentLength]) { + throw new RequestContentLengthMismatchError() + } else { + process.emitWarning(new RequestContentLengthMismatchError()) + } + } + + if (socket[kParser].timeout && socket[kParser].timeoutType === TIMEOUT_HEADERS) { + // istanbul ignore else: only for jest + if (socket[kParser].timeout.refresh) { + socket[kParser].timeout.refresh() + } + } + + resume(client) + } + + destroy (err) { + const { socket, client } = this + + socket[kWriting] = false + + if (err) { + assert(client[kRunning] <= 1, 'pipeline should only contain this request') + util.destroy(socket, err) + } + } +} + +function errorRequest (client, request, err) { + try { + request.onError(err) + assert(request.aborted) + } catch (err) { + client.emit('error', err) + } +} + +module.exports = Client + + +/***/ }), + +/***/ 6436: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +/* istanbul ignore file: only for Node 12 */ + +const { kConnected, kSize } = __nccwpck_require__(2785) + +class CompatWeakRef { + constructor (value) { + this.value = value + } + + deref () { + return this.value[kConnected] === 0 && this.value[kSize] === 0 + ? undefined + : this.value + } +} + +class CompatFinalizer { + constructor (finalizer) { + this.finalizer = finalizer + } + + register (dispatcher, key) { + if (dispatcher.on) { + dispatcher.on('disconnect', () => { + if (dispatcher[kConnected] === 0 && dispatcher[kSize] === 0) { + this.finalizer(key) + } + }) + } + } +} + +module.exports = function () { + // FIXME: remove workaround when the Node bug is fixed + // https://github.com/nodejs/node/issues/49344#issuecomment-1741776308 + if (process.env.NODE_V8_COVERAGE) { + return { + WeakRef: CompatWeakRef, + FinalizationRegistry: CompatFinalizer + } + } + return { + WeakRef: global.WeakRef || CompatWeakRef, + FinalizationRegistry: global.FinalizationRegistry || CompatFinalizer + } +} + + +/***/ }), + +/***/ 663: +/***/ ((module) => { + +"use strict"; + + +// https://wicg.github.io/cookie-store/#cookie-maximum-attribute-value-size +const maxAttributeValueSize = 1024 + +// https://wicg.github.io/cookie-store/#cookie-maximum-name-value-pair-size +const maxNameValuePairSize = 4096 + +module.exports = { + maxAttributeValueSize, + maxNameValuePairSize +} + + +/***/ }), + +/***/ 1724: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { parseSetCookie } = __nccwpck_require__(4408) +const { stringify, getHeadersList } = __nccwpck_require__(3121) +const { webidl } = __nccwpck_require__(1744) +const { Headers } = __nccwpck_require__(554) + +/** + * @typedef {Object} Cookie + * @property {string} name + * @property {string} value + * @property {Date|number|undefined} expires + * @property {number|undefined} maxAge + * @property {string|undefined} domain + * @property {string|undefined} path + * @property {boolean|undefined} secure + * @property {boolean|undefined} httpOnly + * @property {'Strict'|'Lax'|'None'} sameSite + * @property {string[]} unparsed + */ + +/** + * @param {Headers} headers + * @returns {Record} + */ +function getCookies (headers) { + webidl.argumentLengthCheck(arguments, 1, { header: 'getCookies' }) + + webidl.brandCheck(headers, Headers, { strict: false }) + + const cookie = headers.get('cookie') + const out = {} + + if (!cookie) { + return out + } + + for (const piece of cookie.split(';')) { + const [name, ...value] = piece.split('=') + + out[name.trim()] = value.join('=') + } + + return out +} + +/** + * @param {Headers} headers + * @param {string} name + * @param {{ path?: string, domain?: string }|undefined} attributes + * @returns {void} + */ +function deleteCookie (headers, name, attributes) { + webidl.argumentLengthCheck(arguments, 2, { header: 'deleteCookie' }) + + webidl.brandCheck(headers, Headers, { strict: false }) + + name = webidl.converters.DOMString(name) + attributes = webidl.converters.DeleteCookieAttributes(attributes) + + // Matches behavior of + // https://github.com/denoland/deno_std/blob/63827b16330b82489a04614027c33b7904e08be5/http/cookie.ts#L278 + setCookie(headers, { + name, + value: '', + expires: new Date(0), + ...attributes + }) +} + +/** + * @param {Headers} headers + * @returns {Cookie[]} + */ +function getSetCookies (headers) { + webidl.argumentLengthCheck(arguments, 1, { header: 'getSetCookies' }) + + webidl.brandCheck(headers, Headers, { strict: false }) + + const cookies = getHeadersList(headers).cookies + + if (!cookies) { + return [] + } + + // In older versions of undici, cookies is a list of name:value. + return cookies.map((pair) => parseSetCookie(Array.isArray(pair) ? pair[1] : pair)) +} + +/** + * @param {Headers} headers + * @param {Cookie} cookie + * @returns {void} + */ +function setCookie (headers, cookie) { + webidl.argumentLengthCheck(arguments, 2, { header: 'setCookie' }) + + webidl.brandCheck(headers, Headers, { strict: false }) + + cookie = webidl.converters.Cookie(cookie) + + const str = stringify(cookie) + + if (str) { + headers.append('Set-Cookie', stringify(cookie)) + } +} + +webidl.converters.DeleteCookieAttributes = webidl.dictionaryConverter([ + { + converter: webidl.nullableConverter(webidl.converters.DOMString), + key: 'path', + defaultValue: null + }, + { + converter: webidl.nullableConverter(webidl.converters.DOMString), + key: 'domain', + defaultValue: null + } +]) + +webidl.converters.Cookie = webidl.dictionaryConverter([ + { + converter: webidl.converters.DOMString, + key: 'name' + }, + { + converter: webidl.converters.DOMString, + key: 'value' + }, + { + converter: webidl.nullableConverter((value) => { + if (typeof value === 'number') { + return webidl.converters['unsigned long long'](value) + } + + return new Date(value) + }), + key: 'expires', + defaultValue: null + }, + { + converter: webidl.nullableConverter(webidl.converters['long long']), + key: 'maxAge', + defaultValue: null + }, + { + converter: webidl.nullableConverter(webidl.converters.DOMString), + key: 'domain', + defaultValue: null + }, + { + converter: webidl.nullableConverter(webidl.converters.DOMString), + key: 'path', + defaultValue: null + }, + { + converter: webidl.nullableConverter(webidl.converters.boolean), + key: 'secure', + defaultValue: null + }, + { + converter: webidl.nullableConverter(webidl.converters.boolean), + key: 'httpOnly', + defaultValue: null + }, + { + converter: webidl.converters.USVString, + key: 'sameSite', + allowedValues: ['Strict', 'Lax', 'None'] + }, + { + converter: webidl.sequenceConverter(webidl.converters.DOMString), + key: 'unparsed', + defaultValue: [] + } +]) + +module.exports = { + getCookies, + deleteCookie, + getSetCookies, + setCookie +} + + +/***/ }), + +/***/ 4408: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { maxNameValuePairSize, maxAttributeValueSize } = __nccwpck_require__(663) +const { isCTLExcludingHtab } = __nccwpck_require__(3121) +const { collectASequenceOfCodePointsFast } = __nccwpck_require__(685) +const assert = __nccwpck_require__(9491) + +/** + * @description Parses the field-value attributes of a set-cookie header string. + * @see https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4 + * @param {string} header + * @returns if the header is invalid, null will be returned + */ +function parseSetCookie (header) { + // 1. If the set-cookie-string contains a %x00-08 / %x0A-1F / %x7F + // character (CTL characters excluding HTAB): Abort these steps and + // ignore the set-cookie-string entirely. + if (isCTLExcludingHtab(header)) { + return null + } + + let nameValuePair = '' + let unparsedAttributes = '' + let name = '' + let value = '' + + // 2. If the set-cookie-string contains a %x3B (";") character: + if (header.includes(';')) { + // 1. The name-value-pair string consists of the characters up to, + // but not including, the first %x3B (";"), and the unparsed- + // attributes consist of the remainder of the set-cookie-string + // (including the %x3B (";") in question). + const position = { position: 0 } + + nameValuePair = collectASequenceOfCodePointsFast(';', header, position) + unparsedAttributes = header.slice(position.position) + } else { + // Otherwise: + + // 1. The name-value-pair string consists of all the characters + // contained in the set-cookie-string, and the unparsed- + // attributes is the empty string. + nameValuePair = header + } + + // 3. If the name-value-pair string lacks a %x3D ("=") character, then + // the name string is empty, and the value string is the value of + // name-value-pair. + if (!nameValuePair.includes('=')) { + value = nameValuePair + } else { + // Otherwise, the name string consists of the characters up to, but + // not including, the first %x3D ("=") character, and the (possibly + // empty) value string consists of the characters after the first + // %x3D ("=") character. + const position = { position: 0 } + name = collectASequenceOfCodePointsFast( + '=', + nameValuePair, + position + ) + value = nameValuePair.slice(position.position + 1) + } + + // 4. Remove any leading or trailing WSP characters from the name + // string and the value string. + name = name.trim() + value = value.trim() + + // 5. If the sum of the lengths of the name string and the value string + // is more than 4096 octets, abort these steps and ignore the set- + // cookie-string entirely. + if (name.length + value.length > maxNameValuePairSize) { + return null + } + + // 6. The cookie-name is the name string, and the cookie-value is the + // value string. + return { + name, value, ...parseUnparsedAttributes(unparsedAttributes) + } +} + +/** + * Parses the remaining attributes of a set-cookie header + * @see https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4 + * @param {string} unparsedAttributes + * @param {[Object.]={}} cookieAttributeList + */ +function parseUnparsedAttributes (unparsedAttributes, cookieAttributeList = {}) { + // 1. If the unparsed-attributes string is empty, skip the rest of + // these steps. + if (unparsedAttributes.length === 0) { + return cookieAttributeList + } + + // 2. Discard the first character of the unparsed-attributes (which + // will be a %x3B (";") character). + assert(unparsedAttributes[0] === ';') + unparsedAttributes = unparsedAttributes.slice(1) + + let cookieAv = '' + + // 3. If the remaining unparsed-attributes contains a %x3B (";") + // character: + if (unparsedAttributes.includes(';')) { + // 1. Consume the characters of the unparsed-attributes up to, but + // not including, the first %x3B (";") character. + cookieAv = collectASequenceOfCodePointsFast( + ';', + unparsedAttributes, + { position: 0 } + ) + unparsedAttributes = unparsedAttributes.slice(cookieAv.length) + } else { + // Otherwise: + + // 1. Consume the remainder of the unparsed-attributes. + cookieAv = unparsedAttributes + unparsedAttributes = '' + } + + // Let the cookie-av string be the characters consumed in this step. + + let attributeName = '' + let attributeValue = '' + + // 4. If the cookie-av string contains a %x3D ("=") character: + if (cookieAv.includes('=')) { + // 1. The (possibly empty) attribute-name string consists of the + // characters up to, but not including, the first %x3D ("=") + // character, and the (possibly empty) attribute-value string + // consists of the characters after the first %x3D ("=") + // character. + const position = { position: 0 } + + attributeName = collectASequenceOfCodePointsFast( + '=', + cookieAv, + position + ) + attributeValue = cookieAv.slice(position.position + 1) + } else { + // Otherwise: + + // 1. The attribute-name string consists of the entire cookie-av + // string, and the attribute-value string is empty. + attributeName = cookieAv + } + + // 5. Remove any leading or trailing WSP characters from the attribute- + // name string and the attribute-value string. + attributeName = attributeName.trim() + attributeValue = attributeValue.trim() + + // 6. If the attribute-value is longer than 1024 octets, ignore the + // cookie-av string and return to Step 1 of this algorithm. + if (attributeValue.length > maxAttributeValueSize) { + return parseUnparsedAttributes(unparsedAttributes, cookieAttributeList) + } + + // 7. Process the attribute-name and attribute-value according to the + // requirements in the following subsections. (Notice that + // attributes with unrecognized attribute-names are ignored.) + const attributeNameLowercase = attributeName.toLowerCase() + + // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4.1 + // If the attribute-name case-insensitively matches the string + // "Expires", the user agent MUST process the cookie-av as follows. + if (attributeNameLowercase === 'expires') { + // 1. Let the expiry-time be the result of parsing the attribute-value + // as cookie-date (see Section 5.1.1). + const expiryTime = new Date(attributeValue) + + // 2. If the attribute-value failed to parse as a cookie date, ignore + // the cookie-av. + + cookieAttributeList.expires = expiryTime + } else if (attributeNameLowercase === 'max-age') { + // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4.2 + // If the attribute-name case-insensitively matches the string "Max- + // Age", the user agent MUST process the cookie-av as follows. + + // 1. If the first character of the attribute-value is not a DIGIT or a + // "-" character, ignore the cookie-av. + const charCode = attributeValue.charCodeAt(0) + + if ((charCode < 48 || charCode > 57) && attributeValue[0] !== '-') { + return parseUnparsedAttributes(unparsedAttributes, cookieAttributeList) + } + + // 2. If the remainder of attribute-value contains a non-DIGIT + // character, ignore the cookie-av. + if (!/^\d+$/.test(attributeValue)) { + return parseUnparsedAttributes(unparsedAttributes, cookieAttributeList) + } + + // 3. Let delta-seconds be the attribute-value converted to an integer. + const deltaSeconds = Number(attributeValue) + + // 4. Let cookie-age-limit be the maximum age of the cookie (which + // SHOULD be 400 days or less, see Section 4.1.2.2). + + // 5. Set delta-seconds to the smaller of its present value and cookie- + // age-limit. + // deltaSeconds = Math.min(deltaSeconds * 1000, maxExpiresMs) + + // 6. If delta-seconds is less than or equal to zero (0), let expiry- + // time be the earliest representable date and time. Otherwise, let + // the expiry-time be the current date and time plus delta-seconds + // seconds. + // const expiryTime = deltaSeconds <= 0 ? Date.now() : Date.now() + deltaSeconds + + // 7. Append an attribute to the cookie-attribute-list with an + // attribute-name of Max-Age and an attribute-value of expiry-time. + cookieAttributeList.maxAge = deltaSeconds + } else if (attributeNameLowercase === 'domain') { + // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4.3 + // If the attribute-name case-insensitively matches the string "Domain", + // the user agent MUST process the cookie-av as follows. + + // 1. Let cookie-domain be the attribute-value. + let cookieDomain = attributeValue + + // 2. If cookie-domain starts with %x2E ("."), let cookie-domain be + // cookie-domain without its leading %x2E ("."). + if (cookieDomain[0] === '.') { + cookieDomain = cookieDomain.slice(1) + } + + // 3. Convert the cookie-domain to lower case. + cookieDomain = cookieDomain.toLowerCase() + + // 4. Append an attribute to the cookie-attribute-list with an + // attribute-name of Domain and an attribute-value of cookie-domain. + cookieAttributeList.domain = cookieDomain + } else if (attributeNameLowercase === 'path') { + // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4.4 + // If the attribute-name case-insensitively matches the string "Path", + // the user agent MUST process the cookie-av as follows. + + // 1. If the attribute-value is empty or if the first character of the + // attribute-value is not %x2F ("/"): + let cookiePath = '' + if (attributeValue.length === 0 || attributeValue[0] !== '/') { + // 1. Let cookie-path be the default-path. + cookiePath = '/' + } else { + // Otherwise: + + // 1. Let cookie-path be the attribute-value. + cookiePath = attributeValue + } + + // 2. Append an attribute to the cookie-attribute-list with an + // attribute-name of Path and an attribute-value of cookie-path. + cookieAttributeList.path = cookiePath + } else if (attributeNameLowercase === 'secure') { + // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4.5 + // If the attribute-name case-insensitively matches the string "Secure", + // the user agent MUST append an attribute to the cookie-attribute-list + // with an attribute-name of Secure and an empty attribute-value. + + cookieAttributeList.secure = true + } else if (attributeNameLowercase === 'httponly') { + // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4.6 + // If the attribute-name case-insensitively matches the string + // "HttpOnly", the user agent MUST append an attribute to the cookie- + // attribute-list with an attribute-name of HttpOnly and an empty + // attribute-value. + + cookieAttributeList.httpOnly = true + } else if (attributeNameLowercase === 'samesite') { + // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis#section-5.4.7 + // If the attribute-name case-insensitively matches the string + // "SameSite", the user agent MUST process the cookie-av as follows: + + // 1. Let enforcement be "Default". + let enforcement = 'Default' + + const attributeValueLowercase = attributeValue.toLowerCase() + // 2. If cookie-av's attribute-value is a case-insensitive match for + // "None", set enforcement to "None". + if (attributeValueLowercase.includes('none')) { + enforcement = 'None' + } + + // 3. If cookie-av's attribute-value is a case-insensitive match for + // "Strict", set enforcement to "Strict". + if (attributeValueLowercase.includes('strict')) { + enforcement = 'Strict' + } + + // 4. If cookie-av's attribute-value is a case-insensitive match for + // "Lax", set enforcement to "Lax". + if (attributeValueLowercase.includes('lax')) { + enforcement = 'Lax' + } + + // 5. Append an attribute to the cookie-attribute-list with an + // attribute-name of "SameSite" and an attribute-value of + // enforcement. + cookieAttributeList.sameSite = enforcement + } else { + cookieAttributeList.unparsed ??= [] + + cookieAttributeList.unparsed.push(`${attributeName}=${attributeValue}`) + } + + // 8. Return to Step 1 of this algorithm. + return parseUnparsedAttributes(unparsedAttributes, cookieAttributeList) +} + +module.exports = { + parseSetCookie, + parseUnparsedAttributes +} + + +/***/ }), + +/***/ 3121: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const assert = __nccwpck_require__(9491) +const { kHeadersList } = __nccwpck_require__(2785) + +function isCTLExcludingHtab (value) { + if (value.length === 0) { + return false + } + + for (const char of value) { + const code = char.charCodeAt(0) + + if ( + (code >= 0x00 || code <= 0x08) || + (code >= 0x0A || code <= 0x1F) || + code === 0x7F + ) { + return false + } + } +} + +/** + CHAR = + token = 1* + separators = "(" | ")" | "<" | ">" | "@" + | "," | ";" | ":" | "\" | <"> + | "/" | "[" | "]" | "?" | "=" + | "{" | "}" | SP | HT + * @param {string} name + */ +function validateCookieName (name) { + for (const char of name) { + const code = char.charCodeAt(0) + + if ( + (code <= 0x20 || code > 0x7F) || + char === '(' || + char === ')' || + char === '>' || + char === '<' || + char === '@' || + char === ',' || + char === ';' || + char === ':' || + char === '\\' || + char === '"' || + char === '/' || + char === '[' || + char === ']' || + char === '?' || + char === '=' || + char === '{' || + char === '}' + ) { + throw new Error('Invalid cookie name') + } + } +} + +/** + cookie-value = *cookie-octet / ( DQUOTE *cookie-octet DQUOTE ) + cookie-octet = %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E + ; US-ASCII characters excluding CTLs, + ; whitespace DQUOTE, comma, semicolon, + ; and backslash + * @param {string} value + */ +function validateCookieValue (value) { + for (const char of value) { + const code = char.charCodeAt(0) + + if ( + code < 0x21 || // exclude CTLs (0-31) + code === 0x22 || + code === 0x2C || + code === 0x3B || + code === 0x5C || + code > 0x7E // non-ascii + ) { + throw new Error('Invalid header value') + } + } +} + +/** + * path-value = + * @param {string} path + */ +function validateCookiePath (path) { + for (const char of path) { + const code = char.charCodeAt(0) + + if (code < 0x21 || char === ';') { + throw new Error('Invalid cookie path') + } + } +} + +/** + * I have no idea why these values aren't allowed to be honest, + * but Deno tests these. - Khafra + * @param {string} domain + */ +function validateCookieDomain (domain) { + if ( + domain.startsWith('-') || + domain.endsWith('.') || + domain.endsWith('-') + ) { + throw new Error('Invalid cookie domain') + } +} + +/** + * @see https://www.rfc-editor.org/rfc/rfc7231#section-7.1.1.1 + * @param {number|Date} date + IMF-fixdate = day-name "," SP date1 SP time-of-day SP GMT + ; fixed length/zone/capitalization subset of the format + ; see Section 3.3 of [RFC5322] + + day-name = %x4D.6F.6E ; "Mon", case-sensitive + / %x54.75.65 ; "Tue", case-sensitive + / %x57.65.64 ; "Wed", case-sensitive + / %x54.68.75 ; "Thu", case-sensitive + / %x46.72.69 ; "Fri", case-sensitive + / %x53.61.74 ; "Sat", case-sensitive + / %x53.75.6E ; "Sun", case-sensitive + date1 = day SP month SP year + ; e.g., 02 Jun 1982 + + day = 2DIGIT + month = %x4A.61.6E ; "Jan", case-sensitive + / %x46.65.62 ; "Feb", case-sensitive + / %x4D.61.72 ; "Mar", case-sensitive + / %x41.70.72 ; "Apr", case-sensitive + / %x4D.61.79 ; "May", case-sensitive + / %x4A.75.6E ; "Jun", case-sensitive + / %x4A.75.6C ; "Jul", case-sensitive + / %x41.75.67 ; "Aug", case-sensitive + / %x53.65.70 ; "Sep", case-sensitive + / %x4F.63.74 ; "Oct", case-sensitive + / %x4E.6F.76 ; "Nov", case-sensitive + / %x44.65.63 ; "Dec", case-sensitive + year = 4DIGIT + + GMT = %x47.4D.54 ; "GMT", case-sensitive + + time-of-day = hour ":" minute ":" second + ; 00:00:00 - 23:59:60 (leap second) + + hour = 2DIGIT + minute = 2DIGIT + second = 2DIGIT + */ +function toIMFDate (date) { + if (typeof date === 'number') { + date = new Date(date) + } + + const days = [ + 'Sun', 'Mon', 'Tue', 'Wed', + 'Thu', 'Fri', 'Sat' + ] + + const months = [ + 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', + 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' + ] + + const dayName = days[date.getUTCDay()] + const day = date.getUTCDate().toString().padStart(2, '0') + const month = months[date.getUTCMonth()] + const year = date.getUTCFullYear() + const hour = date.getUTCHours().toString().padStart(2, '0') + const minute = date.getUTCMinutes().toString().padStart(2, '0') + const second = date.getUTCSeconds().toString().padStart(2, '0') + + return `${dayName}, ${day} ${month} ${year} ${hour}:${minute}:${second} GMT` +} + +/** + max-age-av = "Max-Age=" non-zero-digit *DIGIT + ; In practice, both expires-av and max-age-av + ; are limited to dates representable by the + ; user agent. + * @param {number} maxAge + */ +function validateCookieMaxAge (maxAge) { + if (maxAge < 0) { + throw new Error('Invalid cookie max-age') + } +} + +/** + * @see https://www.rfc-editor.org/rfc/rfc6265#section-4.1.1 + * @param {import('./index').Cookie} cookie + */ +function stringify (cookie) { + if (cookie.name.length === 0) { + return null + } + + validateCookieName(cookie.name) + validateCookieValue(cookie.value) + + const out = [`${cookie.name}=${cookie.value}`] + + // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-cookie-prefixes-00#section-3.1 + // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-cookie-prefixes-00#section-3.2 + if (cookie.name.startsWith('__Secure-')) { + cookie.secure = true + } + + if (cookie.name.startsWith('__Host-')) { + cookie.secure = true + cookie.domain = null + cookie.path = '/' + } + + if (cookie.secure) { + out.push('Secure') + } + + if (cookie.httpOnly) { + out.push('HttpOnly') + } + + if (typeof cookie.maxAge === 'number') { + validateCookieMaxAge(cookie.maxAge) + out.push(`Max-Age=${cookie.maxAge}`) + } + + if (cookie.domain) { + validateCookieDomain(cookie.domain) + out.push(`Domain=${cookie.domain}`) + } + + if (cookie.path) { + validateCookiePath(cookie.path) + out.push(`Path=${cookie.path}`) + } + + if (cookie.expires && cookie.expires.toString() !== 'Invalid Date') { + out.push(`Expires=${toIMFDate(cookie.expires)}`) + } + + if (cookie.sameSite) { + out.push(`SameSite=${cookie.sameSite}`) + } + + for (const part of cookie.unparsed) { + if (!part.includes('=')) { + throw new Error('Invalid unparsed') + } + + const [key, ...value] = part.split('=') + + out.push(`${key.trim()}=${value.join('=')}`) + } + + return out.join('; ') +} + +let kHeadersListNode + +function getHeadersList (headers) { + if (headers[kHeadersList]) { + return headers[kHeadersList] + } + + if (!kHeadersListNode) { + kHeadersListNode = Object.getOwnPropertySymbols(headers).find( + (symbol) => symbol.description === 'headers list' + ) + + assert(kHeadersListNode, 'Headers cannot be parsed') + } + + const headersList = headers[kHeadersListNode] + assert(headersList) + + return headersList +} + +module.exports = { + isCTLExcludingHtab, + stringify, + getHeadersList +} + + +/***/ }), + +/***/ 2067: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const net = __nccwpck_require__(1808) +const assert = __nccwpck_require__(9491) +const util = __nccwpck_require__(3983) +const { InvalidArgumentError, ConnectTimeoutError } = __nccwpck_require__(8045) + +let tls // include tls conditionally since it is not always available + +// TODO: session re-use does not wait for the first +// connection to resolve the session and might therefore +// resolve the same servername multiple times even when +// re-use is enabled. + +let SessionCache +// FIXME: remove workaround when the Node bug is fixed +// https://github.com/nodejs/node/issues/49344#issuecomment-1741776308 +if (global.FinalizationRegistry && !process.env.NODE_V8_COVERAGE) { + SessionCache = class WeakSessionCache { + constructor (maxCachedSessions) { + this._maxCachedSessions = maxCachedSessions + this._sessionCache = new Map() + this._sessionRegistry = new global.FinalizationRegistry((key) => { + if (this._sessionCache.size < this._maxCachedSessions) { + return + } + + const ref = this._sessionCache.get(key) + if (ref !== undefined && ref.deref() === undefined) { + this._sessionCache.delete(key) + } + }) + } + + get (sessionKey) { + const ref = this._sessionCache.get(sessionKey) + return ref ? ref.deref() : null + } + + set (sessionKey, session) { + if (this._maxCachedSessions === 0) { + return + } + + this._sessionCache.set(sessionKey, new WeakRef(session)) + this._sessionRegistry.register(session, sessionKey) + } + } +} else { + SessionCache = class SimpleSessionCache { + constructor (maxCachedSessions) { + this._maxCachedSessions = maxCachedSessions + this._sessionCache = new Map() + } + + get (sessionKey) { + return this._sessionCache.get(sessionKey) + } + + set (sessionKey, session) { + if (this._maxCachedSessions === 0) { + return + } + + if (this._sessionCache.size >= this._maxCachedSessions) { + // remove the oldest session + const { value: oldestKey } = this._sessionCache.keys().next() + this._sessionCache.delete(oldestKey) + } + + this._sessionCache.set(sessionKey, session) + } + } +} + +function buildConnector ({ allowH2, maxCachedSessions, socketPath, timeout, ...opts }) { + if (maxCachedSessions != null && (!Number.isInteger(maxCachedSessions) || maxCachedSessions < 0)) { + throw new InvalidArgumentError('maxCachedSessions must be a positive integer or zero') + } + + const options = { path: socketPath, ...opts } + const sessionCache = new SessionCache(maxCachedSessions == null ? 100 : maxCachedSessions) + timeout = timeout == null ? 10e3 : timeout + allowH2 = allowH2 != null ? allowH2 : false + return function connect ({ hostname, host, protocol, port, servername, localAddress, httpSocket }, callback) { + let socket + if (protocol === 'https:') { + if (!tls) { + tls = __nccwpck_require__(4404) + } + servername = servername || options.servername || util.getServerName(host) || null + + const sessionKey = servername || hostname + const session = sessionCache.get(sessionKey) || null + + assert(sessionKey) + + socket = tls.connect({ + highWaterMark: 16384, // TLS in node can't have bigger HWM anyway... + ...options, + servername, + session, + localAddress, + // TODO(HTTP/2): Add support for h2c + ALPNProtocols: allowH2 ? ['http/1.1', 'h2'] : ['http/1.1'], + socket: httpSocket, // upgrade socket connection + port: port || 443, + host: hostname + }) + + socket + .on('session', function (session) { + // TODO (fix): Can a session become invalid once established? Don't think so? + sessionCache.set(sessionKey, session) + }) + } else { + assert(!httpSocket, 'httpSocket can only be sent on TLS update') + socket = net.connect({ + highWaterMark: 64 * 1024, // Same as nodejs fs streams. + ...options, + localAddress, + port: port || 80, + host: hostname + }) + } + + // Set TCP keep alive options on the socket here instead of in connect() for the case of assigning the socket + if (options.keepAlive == null || options.keepAlive) { + const keepAliveInitialDelay = options.keepAliveInitialDelay === undefined ? 60e3 : options.keepAliveInitialDelay + socket.setKeepAlive(true, keepAliveInitialDelay) + } + + const cancelTimeout = setupTimeout(() => onConnectTimeout(socket), timeout) + + socket + .setNoDelay(true) + .once(protocol === 'https:' ? 'secureConnect' : 'connect', function () { + cancelTimeout() + + if (callback) { + const cb = callback + callback = null + cb(null, this) + } + }) + .on('error', function (err) { + cancelTimeout() + + if (callback) { + const cb = callback + callback = null + cb(err) + } + }) + + return socket + } +} + +function setupTimeout (onConnectTimeout, timeout) { + if (!timeout) { + return () => {} + } + + let s1 = null + let s2 = null + const timeoutId = setTimeout(() => { + // setImmediate is added to make sure that we priotorise socket error events over timeouts + s1 = setImmediate(() => { + if (process.platform === 'win32') { + // Windows needs an extra setImmediate probably due to implementation differences in the socket logic + s2 = setImmediate(() => onConnectTimeout()) + } else { + onConnectTimeout() + } + }) + }, timeout) + return () => { + clearTimeout(timeoutId) + clearImmediate(s1) + clearImmediate(s2) + } +} + +function onConnectTimeout (socket) { + util.destroy(socket, new ConnectTimeoutError()) +} + +module.exports = buildConnector + + +/***/ }), + +/***/ 8045: +/***/ ((module) => { + +"use strict"; + + +class UndiciError extends Error { + constructor (message) { + super(message) + this.name = 'UndiciError' + this.code = 'UND_ERR' + } +} + +class ConnectTimeoutError extends UndiciError { + constructor (message) { + super(message) + Error.captureStackTrace(this, ConnectTimeoutError) + this.name = 'ConnectTimeoutError' + this.message = message || 'Connect Timeout Error' + this.code = 'UND_ERR_CONNECT_TIMEOUT' + } +} + +class HeadersTimeoutError extends UndiciError { + constructor (message) { + super(message) + Error.captureStackTrace(this, HeadersTimeoutError) + this.name = 'HeadersTimeoutError' + this.message = message || 'Headers Timeout Error' + this.code = 'UND_ERR_HEADERS_TIMEOUT' + } +} + +class HeadersOverflowError extends UndiciError { + constructor (message) { + super(message) + Error.captureStackTrace(this, HeadersOverflowError) + this.name = 'HeadersOverflowError' + this.message = message || 'Headers Overflow Error' + this.code = 'UND_ERR_HEADERS_OVERFLOW' + } +} + +class BodyTimeoutError extends UndiciError { + constructor (message) { + super(message) + Error.captureStackTrace(this, BodyTimeoutError) + this.name = 'BodyTimeoutError' + this.message = message || 'Body Timeout Error' + this.code = 'UND_ERR_BODY_TIMEOUT' + } +} + +class ResponseStatusCodeError extends UndiciError { + constructor (message, statusCode, headers, body) { + super(message) + Error.captureStackTrace(this, ResponseStatusCodeError) + this.name = 'ResponseStatusCodeError' + this.message = message || 'Response Status Code Error' + this.code = 'UND_ERR_RESPONSE_STATUS_CODE' + this.body = body + this.status = statusCode + this.statusCode = statusCode + this.headers = headers + } +} + +class InvalidArgumentError extends UndiciError { + constructor (message) { + super(message) + Error.captureStackTrace(this, InvalidArgumentError) + this.name = 'InvalidArgumentError' + this.message = message || 'Invalid Argument Error' + this.code = 'UND_ERR_INVALID_ARG' + } +} + +class InvalidReturnValueError extends UndiciError { + constructor (message) { + super(message) + Error.captureStackTrace(this, InvalidReturnValueError) + this.name = 'InvalidReturnValueError' + this.message = message || 'Invalid Return Value Error' + this.code = 'UND_ERR_INVALID_RETURN_VALUE' + } +} + +class RequestAbortedError extends UndiciError { + constructor (message) { + super(message) + Error.captureStackTrace(this, RequestAbortedError) + this.name = 'AbortError' + this.message = message || 'Request aborted' + this.code = 'UND_ERR_ABORTED' + } +} + +class InformationalError extends UndiciError { + constructor (message) { + super(message) + Error.captureStackTrace(this, InformationalError) + this.name = 'InformationalError' + this.message = message || 'Request information' + this.code = 'UND_ERR_INFO' + } +} + +class RequestContentLengthMismatchError extends UndiciError { + constructor (message) { + super(message) + Error.captureStackTrace(this, RequestContentLengthMismatchError) + this.name = 'RequestContentLengthMismatchError' + this.message = message || 'Request body length does not match content-length header' + this.code = 'UND_ERR_REQ_CONTENT_LENGTH_MISMATCH' + } +} + +class ResponseContentLengthMismatchError extends UndiciError { + constructor (message) { + super(message) + Error.captureStackTrace(this, ResponseContentLengthMismatchError) + this.name = 'ResponseContentLengthMismatchError' + this.message = message || 'Response body length does not match content-length header' + this.code = 'UND_ERR_RES_CONTENT_LENGTH_MISMATCH' + } +} + +class ClientDestroyedError extends UndiciError { + constructor (message) { + super(message) + Error.captureStackTrace(this, ClientDestroyedError) + this.name = 'ClientDestroyedError' + this.message = message || 'The client is destroyed' + this.code = 'UND_ERR_DESTROYED' + } +} + +class ClientClosedError extends UndiciError { + constructor (message) { + super(message) + Error.captureStackTrace(this, ClientClosedError) + this.name = 'ClientClosedError' + this.message = message || 'The client is closed' + this.code = 'UND_ERR_CLOSED' + } +} + +class SocketError extends UndiciError { + constructor (message, socket) { + super(message) + Error.captureStackTrace(this, SocketError) + this.name = 'SocketError' + this.message = message || 'Socket error' + this.code = 'UND_ERR_SOCKET' + this.socket = socket + } +} + +class NotSupportedError extends UndiciError { + constructor (message) { + super(message) + Error.captureStackTrace(this, NotSupportedError) + this.name = 'NotSupportedError' + this.message = message || 'Not supported error' + this.code = 'UND_ERR_NOT_SUPPORTED' + } +} + +class BalancedPoolMissingUpstreamError extends UndiciError { + constructor (message) { + super(message) + Error.captureStackTrace(this, NotSupportedError) + this.name = 'MissingUpstreamError' + this.message = message || 'No upstream has been added to the BalancedPool' + this.code = 'UND_ERR_BPL_MISSING_UPSTREAM' + } +} + +class HTTPParserError extends Error { + constructor (message, code, data) { + super(message) + Error.captureStackTrace(this, HTTPParserError) + this.name = 'HTTPParserError' + this.code = code ? `HPE_${code}` : undefined + this.data = data ? data.toString() : undefined + } +} + +class ResponseExceededMaxSizeError extends UndiciError { + constructor (message) { + super(message) + Error.captureStackTrace(this, ResponseExceededMaxSizeError) + this.name = 'ResponseExceededMaxSizeError' + this.message = message || 'Response content exceeded max size' + this.code = 'UND_ERR_RES_EXCEEDED_MAX_SIZE' + } +} + +module.exports = { + HTTPParserError, + UndiciError, + HeadersTimeoutError, + HeadersOverflowError, + BodyTimeoutError, + RequestContentLengthMismatchError, + ConnectTimeoutError, + ResponseStatusCodeError, + InvalidArgumentError, + InvalidReturnValueError, + RequestAbortedError, + ClientDestroyedError, + ClientClosedError, + InformationalError, + SocketError, + NotSupportedError, + ResponseContentLengthMismatchError, + BalancedPoolMissingUpstreamError, + ResponseExceededMaxSizeError +} + + +/***/ }), + +/***/ 2905: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { + InvalidArgumentError, + NotSupportedError +} = __nccwpck_require__(8045) +const assert = __nccwpck_require__(9491) +const { kHTTP2BuildRequest, kHTTP2CopyHeaders, kHTTP1BuildRequest } = __nccwpck_require__(2785) +const util = __nccwpck_require__(3983) + +// tokenRegExp and headerCharRegex have been lifted from +// https://github.com/nodejs/node/blob/main/lib/_http_common.js + +/** + * Verifies that the given val is a valid HTTP token + * per the rules defined in RFC 7230 + * See https://tools.ietf.org/html/rfc7230#section-3.2.6 + */ +const tokenRegExp = /^[\^_`a-zA-Z\-0-9!#$%&'*+.|~]+$/ + +/** + * Matches if val contains an invalid field-vchar + * field-value = *( field-content / obs-fold ) + * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] + * field-vchar = VCHAR / obs-text + */ +const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/ + +// Verifies that a given path is valid does not contain control chars \x00 to \x20 +const invalidPathRegex = /[^\u0021-\u00ff]/ + +const kHandler = Symbol('handler') + +const channels = {} + +let extractBody + +try { + const diagnosticsChannel = __nccwpck_require__(7643) + channels.create = diagnosticsChannel.channel('undici:request:create') + channels.bodySent = diagnosticsChannel.channel('undici:request:bodySent') + channels.headers = diagnosticsChannel.channel('undici:request:headers') + channels.trailers = diagnosticsChannel.channel('undici:request:trailers') + channels.error = diagnosticsChannel.channel('undici:request:error') +} catch { + channels.create = { hasSubscribers: false } + channels.bodySent = { hasSubscribers: false } + channels.headers = { hasSubscribers: false } + channels.trailers = { hasSubscribers: false } + channels.error = { hasSubscribers: false } +} + +class Request { + constructor (origin, { + path, + method, + body, + headers, + query, + idempotent, + blocking, + upgrade, + headersTimeout, + bodyTimeout, + reset, + throwOnError, + expectContinue + }, handler) { + if (typeof path !== 'string') { + throw new InvalidArgumentError('path must be a string') + } else if ( + path[0] !== '/' && + !(path.startsWith('http://') || path.startsWith('https://')) && + method !== 'CONNECT' + ) { + throw new InvalidArgumentError('path must be an absolute URL or start with a slash') + } else if (invalidPathRegex.exec(path) !== null) { + throw new InvalidArgumentError('invalid request path') + } + + if (typeof method !== 'string') { + throw new InvalidArgumentError('method must be a string') + } else if (tokenRegExp.exec(method) === null) { + throw new InvalidArgumentError('invalid request method') + } + + if (upgrade && typeof upgrade !== 'string') { + throw new InvalidArgumentError('upgrade must be a string') + } + + if (headersTimeout != null && (!Number.isFinite(headersTimeout) || headersTimeout < 0)) { + throw new InvalidArgumentError('invalid headersTimeout') + } + + if (bodyTimeout != null && (!Number.isFinite(bodyTimeout) || bodyTimeout < 0)) { + throw new InvalidArgumentError('invalid bodyTimeout') + } + + if (reset != null && typeof reset !== 'boolean') { + throw new InvalidArgumentError('invalid reset') + } + + if (expectContinue != null && typeof expectContinue !== 'boolean') { + throw new InvalidArgumentError('invalid expectContinue') + } + + this.headersTimeout = headersTimeout + + this.bodyTimeout = bodyTimeout + + this.throwOnError = throwOnError === true + + this.method = method + + this.abort = null + + if (body == null) { + this.body = null + } else if (util.isStream(body)) { + this.body = body + + const rState = this.body._readableState + if (!rState || !rState.autoDestroy) { + this.endHandler = function autoDestroy () { + util.destroy(this) + } + this.body.on('end', this.endHandler) + } + + this.errorHandler = err => { + if (this.abort) { + this.abort(err) + } else { + this.error = err + } + } + this.body.on('error', this.errorHandler) + } else if (util.isBuffer(body)) { + this.body = body.byteLength ? body : null + } else if (ArrayBuffer.isView(body)) { + this.body = body.buffer.byteLength ? Buffer.from(body.buffer, body.byteOffset, body.byteLength) : null + } else if (body instanceof ArrayBuffer) { + this.body = body.byteLength ? Buffer.from(body) : null + } else if (typeof body === 'string') { + this.body = body.length ? Buffer.from(body) : null + } else if (util.isFormDataLike(body) || util.isIterable(body) || util.isBlobLike(body)) { + this.body = body + } else { + throw new InvalidArgumentError('body must be a string, a Buffer, a Readable stream, an iterable, or an async iterable') + } + + this.completed = false + + this.aborted = false + + this.upgrade = upgrade || null + + this.path = query ? util.buildURL(path, query) : path + + this.origin = origin + + this.idempotent = idempotent == null + ? method === 'HEAD' || method === 'GET' + : idempotent + + this.blocking = blocking == null ? false : blocking + + this.reset = reset == null ? null : reset + + this.host = null + + this.contentLength = null + + this.contentType = null + + this.headers = '' + + // Only for H2 + this.expectContinue = expectContinue != null ? expectContinue : false + + if (Array.isArray(headers)) { + if (headers.length % 2 !== 0) { + throw new InvalidArgumentError('headers array must be even') + } + for (let i = 0; i < headers.length; i += 2) { + processHeader(this, headers[i], headers[i + 1]) + } + } else if (headers && typeof headers === 'object') { + const keys = Object.keys(headers) + for (let i = 0; i < keys.length; i++) { + const key = keys[i] + processHeader(this, key, headers[key]) + } + } else if (headers != null) { + throw new InvalidArgumentError('headers must be an object or an array') + } + + if (util.isFormDataLike(this.body)) { + if (util.nodeMajor < 16 || (util.nodeMajor === 16 && util.nodeMinor < 8)) { + throw new InvalidArgumentError('Form-Data bodies are only supported in node v16.8 and newer.') + } + + if (!extractBody) { + extractBody = (__nccwpck_require__(1472).extractBody) + } + + const [bodyStream, contentType] = extractBody(body) + if (this.contentType == null) { + this.contentType = contentType + this.headers += `content-type: ${contentType}\r\n` + } + this.body = bodyStream.stream + this.contentLength = bodyStream.length + } else if (util.isBlobLike(body) && this.contentType == null && body.type) { + this.contentType = body.type + this.headers += `content-type: ${body.type}\r\n` + } + + util.validateHandler(handler, method, upgrade) + + this.servername = util.getServerName(this.host) + + this[kHandler] = handler + + if (channels.create.hasSubscribers) { + channels.create.publish({ request: this }) + } + } + + onBodySent (chunk) { + if (this[kHandler].onBodySent) { + try { + this[kHandler].onBodySent(chunk) + } catch (err) { + this.onError(err) + } + } + } + + onRequestSent () { + if (channels.bodySent.hasSubscribers) { + channels.bodySent.publish({ request: this }) + } + + if (this[kHandler].onRequestSent) { + try { + this[kHandler].onRequestSent() + } catch (err) { + this.onError(err) + } + } + } + + onConnect (abort) { + assert(!this.aborted) + assert(!this.completed) + + if (this.error) { + abort(this.error) + } else { + this.abort = abort + return this[kHandler].onConnect(abort) + } + } + + onHeaders (statusCode, headers, resume, statusText) { + assert(!this.aborted) + assert(!this.completed) + + if (channels.headers.hasSubscribers) { + channels.headers.publish({ request: this, response: { statusCode, headers, statusText } }) + } + + return this[kHandler].onHeaders(statusCode, headers, resume, statusText) + } + + onData (chunk) { + assert(!this.aborted) + assert(!this.completed) + + return this[kHandler].onData(chunk) + } + + onUpgrade (statusCode, headers, socket) { + assert(!this.aborted) + assert(!this.completed) + + return this[kHandler].onUpgrade(statusCode, headers, socket) + } + + onComplete (trailers) { + this.onFinally() + + assert(!this.aborted) + + this.completed = true + if (channels.trailers.hasSubscribers) { + channels.trailers.publish({ request: this, trailers }) + } + return this[kHandler].onComplete(trailers) + } + + onError (error) { + this.onFinally() + + if (channels.error.hasSubscribers) { + channels.error.publish({ request: this, error }) + } + + if (this.aborted) { + return + } + this.aborted = true + return this[kHandler].onError(error) + } + + onFinally () { + if (this.errorHandler) { + this.body.off('error', this.errorHandler) + this.errorHandler = null + } + + if (this.endHandler) { + this.body.off('end', this.endHandler) + this.endHandler = null + } + } + + // TODO: adjust to support H2 + addHeader (key, value) { + processHeader(this, key, value) + return this + } + + static [kHTTP1BuildRequest] (origin, opts, handler) { + // TODO: Migrate header parsing here, to make Requests + // HTTP agnostic + return new Request(origin, opts, handler) + } + + static [kHTTP2BuildRequest] (origin, opts, handler) { + const headers = opts.headers + opts = { ...opts, headers: null } + + const request = new Request(origin, opts, handler) + + request.headers = {} + + if (Array.isArray(headers)) { + if (headers.length % 2 !== 0) { + throw new InvalidArgumentError('headers array must be even') + } + for (let i = 0; i < headers.length; i += 2) { + processHeader(request, headers[i], headers[i + 1], true) + } + } else if (headers && typeof headers === 'object') { + const keys = Object.keys(headers) + for (let i = 0; i < keys.length; i++) { + const key = keys[i] + processHeader(request, key, headers[key], true) + } + } else if (headers != null) { + throw new InvalidArgumentError('headers must be an object or an array') + } + + return request + } + + static [kHTTP2CopyHeaders] (raw) { + const rawHeaders = raw.split('\r\n') + const headers = {} + + for (const header of rawHeaders) { + const [key, value] = header.split(': ') + + if (value == null || value.length === 0) continue + + if (headers[key]) headers[key] += `,${value}` + else headers[key] = value + } + + return headers + } +} + +function processHeaderValue (key, val, skipAppend) { + if (val && typeof val === 'object') { + throw new InvalidArgumentError(`invalid ${key} header`) + } + + val = val != null ? `${val}` : '' + + if (headerCharRegex.exec(val) !== null) { + throw new InvalidArgumentError(`invalid ${key} header`) + } + + return skipAppend ? val : `${key}: ${val}\r\n` +} + +function processHeader (request, key, val, skipAppend = false) { + if (val && (typeof val === 'object' && !Array.isArray(val))) { + throw new InvalidArgumentError(`invalid ${key} header`) + } else if (val === undefined) { + return + } + + if ( + request.host === null && + key.length === 4 && + key.toLowerCase() === 'host' + ) { + if (headerCharRegex.exec(val) !== null) { + throw new InvalidArgumentError(`invalid ${key} header`) + } + // Consumed by Client + request.host = val + } else if ( + request.contentLength === null && + key.length === 14 && + key.toLowerCase() === 'content-length' + ) { + request.contentLength = parseInt(val, 10) + if (!Number.isFinite(request.contentLength)) { + throw new InvalidArgumentError('invalid content-length header') + } + } else if ( + request.contentType === null && + key.length === 12 && + key.toLowerCase() === 'content-type' + ) { + request.contentType = val + if (skipAppend) request.headers[key] = processHeaderValue(key, val, skipAppend) + else request.headers += processHeaderValue(key, val) + } else if ( + key.length === 17 && + key.toLowerCase() === 'transfer-encoding' + ) { + throw new InvalidArgumentError('invalid transfer-encoding header') + } else if ( + key.length === 10 && + key.toLowerCase() === 'connection' + ) { + const value = typeof val === 'string' ? val.toLowerCase() : null + if (value !== 'close' && value !== 'keep-alive') { + throw new InvalidArgumentError('invalid connection header') + } else if (value === 'close') { + request.reset = true + } + } else if ( + key.length === 10 && + key.toLowerCase() === 'keep-alive' + ) { + throw new InvalidArgumentError('invalid keep-alive header') + } else if ( + key.length === 7 && + key.toLowerCase() === 'upgrade' + ) { + throw new InvalidArgumentError('invalid upgrade header') + } else if ( + key.length === 6 && + key.toLowerCase() === 'expect' + ) { + throw new NotSupportedError('expect header not supported') + } else if (tokenRegExp.exec(key) === null) { + throw new InvalidArgumentError('invalid header key') + } else { + if (Array.isArray(val)) { + for (let i = 0; i < val.length; i++) { + if (skipAppend) { + if (request.headers[key]) request.headers[key] += `,${processHeaderValue(key, val[i], skipAppend)}` + else request.headers[key] = processHeaderValue(key, val[i], skipAppend) + } else { + request.headers += processHeaderValue(key, val[i]) + } + } + } else { + if (skipAppend) request.headers[key] = processHeaderValue(key, val, skipAppend) + else request.headers += processHeaderValue(key, val) + } + } +} + +module.exports = Request + + +/***/ }), + +/***/ 2785: +/***/ ((module) => { + +module.exports = { + kClose: Symbol('close'), + kDestroy: Symbol('destroy'), + kDispatch: Symbol('dispatch'), + kUrl: Symbol('url'), + kWriting: Symbol('writing'), + kResuming: Symbol('resuming'), + kQueue: Symbol('queue'), + kConnect: Symbol('connect'), + kConnecting: Symbol('connecting'), + kHeadersList: Symbol('headers list'), + kKeepAliveDefaultTimeout: Symbol('default keep alive timeout'), + kKeepAliveMaxTimeout: Symbol('max keep alive timeout'), + kKeepAliveTimeoutThreshold: Symbol('keep alive timeout threshold'), + kKeepAliveTimeoutValue: Symbol('keep alive timeout'), + kKeepAlive: Symbol('keep alive'), + kHeadersTimeout: Symbol('headers timeout'), + kBodyTimeout: Symbol('body timeout'), + kServerName: Symbol('server name'), + kLocalAddress: Symbol('local address'), + kHost: Symbol('host'), + kNoRef: Symbol('no ref'), + kBodyUsed: Symbol('used'), + kRunning: Symbol('running'), + kBlocking: Symbol('blocking'), + kPending: Symbol('pending'), + kSize: Symbol('size'), + kBusy: Symbol('busy'), + kQueued: Symbol('queued'), + kFree: Symbol('free'), + kConnected: Symbol('connected'), + kClosed: Symbol('closed'), + kNeedDrain: Symbol('need drain'), + kReset: Symbol('reset'), + kDestroyed: Symbol.for('nodejs.stream.destroyed'), + kMaxHeadersSize: Symbol('max headers size'), + kRunningIdx: Symbol('running index'), + kPendingIdx: Symbol('pending index'), + kError: Symbol('error'), + kClients: Symbol('clients'), + kClient: Symbol('client'), + kParser: Symbol('parser'), + kOnDestroyed: Symbol('destroy callbacks'), + kPipelining: Symbol('pipelining'), + kSocket: Symbol('socket'), + kHostHeader: Symbol('host header'), + kConnector: Symbol('connector'), + kStrictContentLength: Symbol('strict content length'), + kMaxRedirections: Symbol('maxRedirections'), + kMaxRequests: Symbol('maxRequestsPerClient'), + kProxy: Symbol('proxy agent options'), + kCounter: Symbol('socket request counter'), + kInterceptors: Symbol('dispatch interceptors'), + kMaxResponseSize: Symbol('max response size'), + kHTTP2Session: Symbol('http2Session'), + kHTTP2SessionState: Symbol('http2Session state'), + kHTTP2BuildRequest: Symbol('http2 build request'), + kHTTP1BuildRequest: Symbol('http1 build request'), + kHTTP2CopyHeaders: Symbol('http2 copy headers'), + kHTTPConnVersion: Symbol('http connection version') +} + + +/***/ }), + +/***/ 3983: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const assert = __nccwpck_require__(9491) +const { kDestroyed, kBodyUsed } = __nccwpck_require__(2785) +const { IncomingMessage } = __nccwpck_require__(3685) +const stream = __nccwpck_require__(2781) +const net = __nccwpck_require__(1808) +const { InvalidArgumentError } = __nccwpck_require__(8045) +const { Blob } = __nccwpck_require__(4300) +const nodeUtil = __nccwpck_require__(3837) +const { stringify } = __nccwpck_require__(3477) + +const [nodeMajor, nodeMinor] = process.versions.node.split('.').map(v => Number(v)) + +function nop () {} + +function isStream (obj) { + return obj && typeof obj === 'object' && typeof obj.pipe === 'function' && typeof obj.on === 'function' +} + +// based on https://github.com/node-fetch/fetch-blob/blob/8ab587d34080de94140b54f07168451e7d0b655e/index.js#L229-L241 (MIT License) +function isBlobLike (object) { + return (Blob && object instanceof Blob) || ( + object && + typeof object === 'object' && + (typeof object.stream === 'function' || + typeof object.arrayBuffer === 'function') && + /^(Blob|File)$/.test(object[Symbol.toStringTag]) + ) +} + +function buildURL (url, queryParams) { + if (url.includes('?') || url.includes('#')) { + throw new Error('Query params cannot be passed when url already contains "?" or "#".') + } + + const stringified = stringify(queryParams) + + if (stringified) { + url += '?' + stringified + } + + return url +} + +function parseURL (url) { + if (typeof url === 'string') { + url = new URL(url) + + if (!/^https?:/.test(url.origin || url.protocol)) { + throw new InvalidArgumentError('Invalid URL protocol: the URL must start with `http:` or `https:`.') + } + + return url + } + + if (!url || typeof url !== 'object') { + throw new InvalidArgumentError('Invalid URL: The URL argument must be a non-null object.') + } + + if (!/^https?:/.test(url.origin || url.protocol)) { + throw new InvalidArgumentError('Invalid URL protocol: the URL must start with `http:` or `https:`.') + } + + if (!(url instanceof URL)) { + if (url.port != null && url.port !== '' && !Number.isFinite(parseInt(url.port))) { + throw new InvalidArgumentError('Invalid URL: port must be a valid integer or a string representation of an integer.') + } + + if (url.path != null && typeof url.path !== 'string') { + throw new InvalidArgumentError('Invalid URL path: the path must be a string or null/undefined.') + } + + if (url.pathname != null && typeof url.pathname !== 'string') { + throw new InvalidArgumentError('Invalid URL pathname: the pathname must be a string or null/undefined.') + } + + if (url.hostname != null && typeof url.hostname !== 'string') { + throw new InvalidArgumentError('Invalid URL hostname: the hostname must be a string or null/undefined.') + } + + if (url.origin != null && typeof url.origin !== 'string') { + throw new InvalidArgumentError('Invalid URL origin: the origin must be a string or null/undefined.') + } + + const port = url.port != null + ? url.port + : (url.protocol === 'https:' ? 443 : 80) + let origin = url.origin != null + ? url.origin + : `${url.protocol}//${url.hostname}:${port}` + let path = url.path != null + ? url.path + : `${url.pathname || ''}${url.search || ''}` + + if (origin.endsWith('/')) { + origin = origin.substring(0, origin.length - 1) + } + + if (path && !path.startsWith('/')) { + path = `/${path}` + } + // new URL(path, origin) is unsafe when `path` contains an absolute URL + // From https://developer.mozilla.org/en-US/docs/Web/API/URL/URL: + // If first parameter is a relative URL, second param is required, and will be used as the base URL. + // If first parameter is an absolute URL, a given second param will be ignored. + url = new URL(origin + path) + } + + return url +} + +function parseOrigin (url) { + url = parseURL(url) + + if (url.pathname !== '/' || url.search || url.hash) { + throw new InvalidArgumentError('invalid url') + } + + return url +} + +function getHostname (host) { + if (host[0] === '[') { + const idx = host.indexOf(']') + + assert(idx !== -1) + return host.substr(1, idx - 1) + } + + const idx = host.indexOf(':') + if (idx === -1) return host + + return host.substr(0, idx) +} + +// IP addresses are not valid server names per RFC6066 +// > Currently, the only server names supported are DNS hostnames +function getServerName (host) { + if (!host) { + return null + } + + assert.strictEqual(typeof host, 'string') + + const servername = getHostname(host) + if (net.isIP(servername)) { + return '' + } + + return servername +} + +function deepClone (obj) { + return JSON.parse(JSON.stringify(obj)) +} + +function isAsyncIterable (obj) { + return !!(obj != null && typeof obj[Symbol.asyncIterator] === 'function') +} + +function isIterable (obj) { + return !!(obj != null && (typeof obj[Symbol.iterator] === 'function' || typeof obj[Symbol.asyncIterator] === 'function')) +} + +function bodyLength (body) { + if (body == null) { + return 0 + } else if (isStream(body)) { + const state = body._readableState + return state && state.objectMode === false && state.ended === true && Number.isFinite(state.length) + ? state.length + : null + } else if (isBlobLike(body)) { + return body.size != null ? body.size : null + } else if (isBuffer(body)) { + return body.byteLength + } + + return null +} + +function isDestroyed (stream) { + return !stream || !!(stream.destroyed || stream[kDestroyed]) +} + +function isReadableAborted (stream) { + const state = stream && stream._readableState + return isDestroyed(stream) && state && !state.endEmitted +} + +function destroy (stream, err) { + if (stream == null || !isStream(stream) || isDestroyed(stream)) { + return + } + + if (typeof stream.destroy === 'function') { + if (Object.getPrototypeOf(stream).constructor === IncomingMessage) { + // See: https://github.com/nodejs/node/pull/38505/files + stream.socket = null + } + + stream.destroy(err) + } else if (err) { + process.nextTick((stream, err) => { + stream.emit('error', err) + }, stream, err) + } + + if (stream.destroyed !== true) { + stream[kDestroyed] = true + } +} + +const KEEPALIVE_TIMEOUT_EXPR = /timeout=(\d+)/ +function parseKeepAliveTimeout (val) { + const m = val.toString().match(KEEPALIVE_TIMEOUT_EXPR) + return m ? parseInt(m[1], 10) * 1000 : null +} + +function parseHeaders (headers, obj = {}) { + // For H2 support + if (!Array.isArray(headers)) return headers + + for (let i = 0; i < headers.length; i += 2) { + const key = headers[i].toString().toLowerCase() + let val = obj[key] + + if (!val) { + if (Array.isArray(headers[i + 1])) { + obj[key] = headers[i + 1] + } else { + obj[key] = headers[i + 1].toString('utf8') + } + } else { + if (!Array.isArray(val)) { + val = [val] + obj[key] = val + } + val.push(headers[i + 1].toString('utf8')) + } + } + + // See https://github.com/nodejs/node/pull/46528 + if ('content-length' in obj && 'content-disposition' in obj) { + obj['content-disposition'] = Buffer.from(obj['content-disposition']).toString('latin1') + } + + return obj +} + +function parseRawHeaders (headers) { + const ret = [] + let hasContentLength = false + let contentDispositionIdx = -1 + + for (let n = 0; n < headers.length; n += 2) { + const key = headers[n + 0].toString() + const val = headers[n + 1].toString('utf8') + + if (key.length === 14 && (key === 'content-length' || key.toLowerCase() === 'content-length')) { + ret.push(key, val) + hasContentLength = true + } else if (key.length === 19 && (key === 'content-disposition' || key.toLowerCase() === 'content-disposition')) { + contentDispositionIdx = ret.push(key, val) - 1 + } else { + ret.push(key, val) + } + } + + // See https://github.com/nodejs/node/pull/46528 + if (hasContentLength && contentDispositionIdx !== -1) { + ret[contentDispositionIdx] = Buffer.from(ret[contentDispositionIdx]).toString('latin1') + } + + return ret +} + +function isBuffer (buffer) { + // See, https://github.com/mcollina/undici/pull/319 + return buffer instanceof Uint8Array || Buffer.isBuffer(buffer) +} + +function validateHandler (handler, method, upgrade) { + if (!handler || typeof handler !== 'object') { + throw new InvalidArgumentError('handler must be an object') + } + + if (typeof handler.onConnect !== 'function') { + throw new InvalidArgumentError('invalid onConnect method') + } + + if (typeof handler.onError !== 'function') { + throw new InvalidArgumentError('invalid onError method') + } + + if (typeof handler.onBodySent !== 'function' && handler.onBodySent !== undefined) { + throw new InvalidArgumentError('invalid onBodySent method') + } + + if (upgrade || method === 'CONNECT') { + if (typeof handler.onUpgrade !== 'function') { + throw new InvalidArgumentError('invalid onUpgrade method') + } + } else { + if (typeof handler.onHeaders !== 'function') { + throw new InvalidArgumentError('invalid onHeaders method') + } + + if (typeof handler.onData !== 'function') { + throw new InvalidArgumentError('invalid onData method') + } + + if (typeof handler.onComplete !== 'function') { + throw new InvalidArgumentError('invalid onComplete method') + } + } +} + +// A body is disturbed if it has been read from and it cannot +// be re-used without losing state or data. +function isDisturbed (body) { + return !!(body && ( + stream.isDisturbed + ? stream.isDisturbed(body) || body[kBodyUsed] // TODO (fix): Why is body[kBodyUsed] needed? + : body[kBodyUsed] || + body.readableDidRead || + (body._readableState && body._readableState.dataEmitted) || + isReadableAborted(body) + )) +} + +function isErrored (body) { + return !!(body && ( + stream.isErrored + ? stream.isErrored(body) + : /state: 'errored'/.test(nodeUtil.inspect(body) + ))) +} + +function isReadable (body) { + return !!(body && ( + stream.isReadable + ? stream.isReadable(body) + : /state: 'readable'/.test(nodeUtil.inspect(body) + ))) +} + +function getSocketInfo (socket) { + return { + localAddress: socket.localAddress, + localPort: socket.localPort, + remoteAddress: socket.remoteAddress, + remotePort: socket.remotePort, + remoteFamily: socket.remoteFamily, + timeout: socket.timeout, + bytesWritten: socket.bytesWritten, + bytesRead: socket.bytesRead + } +} + +async function * convertIterableToBuffer (iterable) { + for await (const chunk of iterable) { + yield Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk) + } +} + +let ReadableStream +function ReadableStreamFrom (iterable) { + if (!ReadableStream) { + ReadableStream = (__nccwpck_require__(5356).ReadableStream) + } + + if (ReadableStream.from) { + return ReadableStream.from(convertIterableToBuffer(iterable)) + } + + let iterator + return new ReadableStream( + { + async start () { + iterator = iterable[Symbol.asyncIterator]() + }, + async pull (controller) { + const { done, value } = await iterator.next() + if (done) { + queueMicrotask(() => { + controller.close() + }) + } else { + const buf = Buffer.isBuffer(value) ? value : Buffer.from(value) + controller.enqueue(new Uint8Array(buf)) + } + return controller.desiredSize > 0 + }, + async cancel (reason) { + await iterator.return() + } + }, + 0 + ) +} + +// The chunk should be a FormData instance and contains +// all the required methods. +function isFormDataLike (object) { + return ( + object && + typeof object === 'object' && + typeof object.append === 'function' && + typeof object.delete === 'function' && + typeof object.get === 'function' && + typeof object.getAll === 'function' && + typeof object.has === 'function' && + typeof object.set === 'function' && + object[Symbol.toStringTag] === 'FormData' + ) +} + +function throwIfAborted (signal) { + if (!signal) { return } + if (typeof signal.throwIfAborted === 'function') { + signal.throwIfAborted() + } else { + if (signal.aborted) { + // DOMException not available < v17.0.0 + const err = new Error('The operation was aborted') + err.name = 'AbortError' + throw err + } + } +} + +let events +function addAbortListener (signal, listener) { + if (typeof Symbol.dispose === 'symbol') { + if (!events) { + events = __nccwpck_require__(2361) + } + if (typeof events.addAbortListener === 'function' && 'aborted' in signal) { + return events.addAbortListener(signal, listener) + } + } + if ('addEventListener' in signal) { + signal.addEventListener('abort', listener, { once: true }) + return () => signal.removeEventListener('abort', listener) + } + signal.addListener('abort', listener) + return () => signal.removeListener('abort', listener) +} + +const hasToWellFormed = !!String.prototype.toWellFormed + +/** + * @param {string} val + */ +function toUSVString (val) { + if (hasToWellFormed) { + return `${val}`.toWellFormed() + } else if (nodeUtil.toUSVString) { + return nodeUtil.toUSVString(val) + } + + return `${val}` +} + +const kEnumerableProperty = Object.create(null) +kEnumerableProperty.enumerable = true + +module.exports = { + kEnumerableProperty, + nop, + isDisturbed, + isErrored, + isReadable, + toUSVString, + isReadableAborted, + isBlobLike, + parseOrigin, + parseURL, + getServerName, + isStream, + isIterable, + isAsyncIterable, + isDestroyed, + parseRawHeaders, + parseHeaders, + parseKeepAliveTimeout, + destroy, + bodyLength, + deepClone, + ReadableStreamFrom, + isBuffer, + validateHandler, + getSocketInfo, + isFormDataLike, + buildURL, + throwIfAborted, + addAbortListener, + nodeMajor, + nodeMinor, + nodeHasAutoSelectFamily: nodeMajor > 18 || (nodeMajor === 18 && nodeMinor >= 13) +} + + +/***/ }), + +/***/ 4839: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const Dispatcher = __nccwpck_require__(412) +const { + ClientDestroyedError, + ClientClosedError, + InvalidArgumentError +} = __nccwpck_require__(8045) +const { kDestroy, kClose, kDispatch, kInterceptors } = __nccwpck_require__(2785) + +const kDestroyed = Symbol('destroyed') +const kClosed = Symbol('closed') +const kOnDestroyed = Symbol('onDestroyed') +const kOnClosed = Symbol('onClosed') +const kInterceptedDispatch = Symbol('Intercepted Dispatch') + +class DispatcherBase extends Dispatcher { + constructor () { + super() + + this[kDestroyed] = false + this[kOnDestroyed] = null + this[kClosed] = false + this[kOnClosed] = [] + } + + get destroyed () { + return this[kDestroyed] + } + + get closed () { + return this[kClosed] + } + + get interceptors () { + return this[kInterceptors] + } + + set interceptors (newInterceptors) { + if (newInterceptors) { + for (let i = newInterceptors.length - 1; i >= 0; i--) { + const interceptor = this[kInterceptors][i] + if (typeof interceptor !== 'function') { + throw new InvalidArgumentError('interceptor must be an function') + } + } + } + + this[kInterceptors] = newInterceptors + } + + close (callback) { + if (callback === undefined) { + return new Promise((resolve, reject) => { + this.close((err, data) => { + return err ? reject(err) : resolve(data) + }) + }) + } + + if (typeof callback !== 'function') { + throw new InvalidArgumentError('invalid callback') + } + + if (this[kDestroyed]) { + queueMicrotask(() => callback(new ClientDestroyedError(), null)) + return + } + + if (this[kClosed]) { + if (this[kOnClosed]) { + this[kOnClosed].push(callback) + } else { + queueMicrotask(() => callback(null, null)) + } + return + } + + this[kClosed] = true + this[kOnClosed].push(callback) + + const onClosed = () => { + const callbacks = this[kOnClosed] + this[kOnClosed] = null + for (let i = 0; i < callbacks.length; i++) { + callbacks[i](null, null) + } + } + + // Should not error. + this[kClose]() + .then(() => this.destroy()) + .then(() => { + queueMicrotask(onClosed) + }) + } + + destroy (err, callback) { + if (typeof err === 'function') { + callback = err + err = null + } + + if (callback === undefined) { + return new Promise((resolve, reject) => { + this.destroy(err, (err, data) => { + return err ? /* istanbul ignore next: should never error */ reject(err) : resolve(data) + }) + }) + } + + if (typeof callback !== 'function') { + throw new InvalidArgumentError('invalid callback') + } + + if (this[kDestroyed]) { + if (this[kOnDestroyed]) { + this[kOnDestroyed].push(callback) + } else { + queueMicrotask(() => callback(null, null)) + } + return + } + + if (!err) { + err = new ClientDestroyedError() + } + + this[kDestroyed] = true + this[kOnDestroyed] = this[kOnDestroyed] || [] + this[kOnDestroyed].push(callback) + + const onDestroyed = () => { + const callbacks = this[kOnDestroyed] + this[kOnDestroyed] = null + for (let i = 0; i < callbacks.length; i++) { + callbacks[i](null, null) + } + } + + // Should not error. + this[kDestroy](err).then(() => { + queueMicrotask(onDestroyed) + }) + } + + [kInterceptedDispatch] (opts, handler) { + if (!this[kInterceptors] || this[kInterceptors].length === 0) { + this[kInterceptedDispatch] = this[kDispatch] + return this[kDispatch](opts, handler) + } + + let dispatch = this[kDispatch].bind(this) + for (let i = this[kInterceptors].length - 1; i >= 0; i--) { + dispatch = this[kInterceptors][i](dispatch) + } + this[kInterceptedDispatch] = dispatch + return dispatch(opts, handler) + } + + dispatch (opts, handler) { + if (!handler || typeof handler !== 'object') { + throw new InvalidArgumentError('handler must be an object') + } + + try { + if (!opts || typeof opts !== 'object') { + throw new InvalidArgumentError('opts must be an object.') + } + + if (this[kDestroyed] || this[kOnDestroyed]) { + throw new ClientDestroyedError() + } + + if (this[kClosed]) { + throw new ClientClosedError() + } + + return this[kInterceptedDispatch](opts, handler) + } catch (err) { + if (typeof handler.onError !== 'function') { + throw new InvalidArgumentError('invalid onError method') + } + + handler.onError(err) + + return false + } + } +} + +module.exports = DispatcherBase + + +/***/ }), + +/***/ 412: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const EventEmitter = __nccwpck_require__(2361) + +class Dispatcher extends EventEmitter { + dispatch () { + throw new Error('not implemented') + } + + close () { + throw new Error('not implemented') + } + + destroy () { + throw new Error('not implemented') + } +} + +module.exports = Dispatcher + + +/***/ }), + +/***/ 1472: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const Busboy = __nccwpck_require__(3438) +const util = __nccwpck_require__(3983) +const { + ReadableStreamFrom, + isBlobLike, + isReadableStreamLike, + readableStreamClose, + createDeferredPromise, + fullyReadBody +} = __nccwpck_require__(2538) +const { FormData } = __nccwpck_require__(2015) +const { kState } = __nccwpck_require__(5861) +const { webidl } = __nccwpck_require__(1744) +const { DOMException, structuredClone } = __nccwpck_require__(1037) +const { Blob, File: NativeFile } = __nccwpck_require__(4300) +const { kBodyUsed } = __nccwpck_require__(2785) +const assert = __nccwpck_require__(9491) +const { isErrored } = __nccwpck_require__(3983) +const { isUint8Array, isArrayBuffer } = __nccwpck_require__(9830) +const { File: UndiciFile } = __nccwpck_require__(8511) +const { parseMIMEType, serializeAMimeType } = __nccwpck_require__(685) + +let ReadableStream = globalThis.ReadableStream + +/** @type {globalThis['File']} */ +const File = NativeFile ?? UndiciFile +const textEncoder = new TextEncoder() +const textDecoder = new TextDecoder() + +// https://fetch.spec.whatwg.org/#concept-bodyinit-extract +function extractBody (object, keepalive = false) { + if (!ReadableStream) { + ReadableStream = (__nccwpck_require__(5356).ReadableStream) + } + + // 1. Let stream be null. + let stream = null + + // 2. If object is a ReadableStream object, then set stream to object. + if (object instanceof ReadableStream) { + stream = object + } else if (isBlobLike(object)) { + // 3. Otherwise, if object is a Blob object, set stream to the + // result of running object’s get stream. + stream = object.stream() + } else { + // 4. Otherwise, set stream to a new ReadableStream object, and set + // up stream. + stream = new ReadableStream({ + async pull (controller) { + controller.enqueue( + typeof source === 'string' ? textEncoder.encode(source) : source + ) + queueMicrotask(() => readableStreamClose(controller)) + }, + start () {}, + type: undefined + }) + } + + // 5. Assert: stream is a ReadableStream object. + assert(isReadableStreamLike(stream)) + + // 6. Let action be null. + let action = null + + // 7. Let source be null. + let source = null + + // 8. Let length be null. + let length = null + + // 9. Let type be null. + let type = null + + // 10. Switch on object: + if (typeof object === 'string') { + // Set source to the UTF-8 encoding of object. + // Note: setting source to a Uint8Array here breaks some mocking assumptions. + source = object + + // Set type to `text/plain;charset=UTF-8`. + type = 'text/plain;charset=UTF-8' + } else if (object instanceof URLSearchParams) { + // URLSearchParams + + // spec says to run application/x-www-form-urlencoded on body.list + // this is implemented in Node.js as apart of an URLSearchParams instance toString method + // See: https://github.com/nodejs/node/blob/e46c680bf2b211bbd52cf959ca17ee98c7f657f5/lib/internal/url.js#L490 + // and https://github.com/nodejs/node/blob/e46c680bf2b211bbd52cf959ca17ee98c7f657f5/lib/internal/url.js#L1100 + + // Set source to the result of running the application/x-www-form-urlencoded serializer with object’s list. + source = object.toString() + + // Set type to `application/x-www-form-urlencoded;charset=UTF-8`. + type = 'application/x-www-form-urlencoded;charset=UTF-8' + } else if (isArrayBuffer(object)) { + // BufferSource/ArrayBuffer + + // Set source to a copy of the bytes held by object. + source = new Uint8Array(object.slice()) + } else if (ArrayBuffer.isView(object)) { + // BufferSource/ArrayBufferView + + // Set source to a copy of the bytes held by object. + source = new Uint8Array(object.buffer.slice(object.byteOffset, object.byteOffset + object.byteLength)) + } else if (util.isFormDataLike(object)) { + const boundary = `----formdata-undici-0${`${Math.floor(Math.random() * 1e11)}`.padStart(11, '0')}` + const prefix = `--${boundary}\r\nContent-Disposition: form-data` + + /*! formdata-polyfill. MIT License. Jimmy Wärting */ + const escape = (str) => + str.replace(/\n/g, '%0A').replace(/\r/g, '%0D').replace(/"/g, '%22') + const normalizeLinefeeds = (value) => value.replace(/\r?\n|\r/g, '\r\n') + + // Set action to this step: run the multipart/form-data + // encoding algorithm, with object’s entry list and UTF-8. + // - This ensures that the body is immutable and can't be changed afterwords + // - That the content-length is calculated in advance. + // - And that all parts are pre-encoded and ready to be sent. + + const blobParts = [] + const rn = new Uint8Array([13, 10]) // '\r\n' + length = 0 + let hasUnknownSizeValue = false + + for (const [name, value] of object) { + if (typeof value === 'string') { + const chunk = textEncoder.encode(prefix + + `; name="${escape(normalizeLinefeeds(name))}"` + + `\r\n\r\n${normalizeLinefeeds(value)}\r\n`) + blobParts.push(chunk) + length += chunk.byteLength + } else { + const chunk = textEncoder.encode(`${prefix}; name="${escape(normalizeLinefeeds(name))}"` + + (value.name ? `; filename="${escape(value.name)}"` : '') + '\r\n' + + `Content-Type: ${ + value.type || 'application/octet-stream' + }\r\n\r\n`) + blobParts.push(chunk, value, rn) + if (typeof value.size === 'number') { + length += chunk.byteLength + value.size + rn.byteLength + } else { + hasUnknownSizeValue = true + } + } + } + + const chunk = textEncoder.encode(`--${boundary}--`) + blobParts.push(chunk) + length += chunk.byteLength + if (hasUnknownSizeValue) { + length = null + } + + // Set source to object. + source = object + + action = async function * () { + for (const part of blobParts) { + if (part.stream) { + yield * part.stream() + } else { + yield part + } + } + } + + // Set type to `multipart/form-data; boundary=`, + // followed by the multipart/form-data boundary string generated + // by the multipart/form-data encoding algorithm. + type = 'multipart/form-data; boundary=' + boundary + } else if (isBlobLike(object)) { + // Blob + + // Set source to object. + source = object + + // Set length to object’s size. + length = object.size + + // If object’s type attribute is not the empty byte sequence, set + // type to its value. + if (object.type) { + type = object.type + } + } else if (typeof object[Symbol.asyncIterator] === 'function') { + // If keepalive is true, then throw a TypeError. + if (keepalive) { + throw new TypeError('keepalive') + } + + // If object is disturbed or locked, then throw a TypeError. + if (util.isDisturbed(object) || object.locked) { + throw new TypeError( + 'Response body object should not be disturbed or locked' + ) + } + + stream = + object instanceof ReadableStream ? object : ReadableStreamFrom(object) + } + + // 11. If source is a byte sequence, then set action to a + // step that returns source and length to source’s length. + if (typeof source === 'string' || util.isBuffer(source)) { + length = Buffer.byteLength(source) + } + + // 12. If action is non-null, then run these steps in in parallel: + if (action != null) { + // Run action. + let iterator + stream = new ReadableStream({ + async start () { + iterator = action(object)[Symbol.asyncIterator]() + }, + async pull (controller) { + const { value, done } = await iterator.next() + if (done) { + // When running action is done, close stream. + queueMicrotask(() => { + controller.close() + }) + } else { + // Whenever one or more bytes are available and stream is not errored, + // enqueue a Uint8Array wrapping an ArrayBuffer containing the available + // bytes into stream. + if (!isErrored(stream)) { + controller.enqueue(new Uint8Array(value)) + } + } + return controller.desiredSize > 0 + }, + async cancel (reason) { + await iterator.return() + }, + type: undefined + }) + } + + // 13. Let body be a body whose stream is stream, source is source, + // and length is length. + const body = { stream, source, length } + + // 14. Return (body, type). + return [body, type] +} + +// https://fetch.spec.whatwg.org/#bodyinit-safely-extract +function safelyExtractBody (object, keepalive = false) { + if (!ReadableStream) { + // istanbul ignore next + ReadableStream = (__nccwpck_require__(5356).ReadableStream) + } + + // To safely extract a body and a `Content-Type` value from + // a byte sequence or BodyInit object object, run these steps: + + // 1. If object is a ReadableStream object, then: + if (object instanceof ReadableStream) { + // Assert: object is neither disturbed nor locked. + // istanbul ignore next + assert(!util.isDisturbed(object), 'The body has already been consumed.') + // istanbul ignore next + assert(!object.locked, 'The stream is locked.') + } + + // 2. Return the results of extracting object. + return extractBody(object, keepalive) +} + +function cloneBody (body) { + // To clone a body body, run these steps: + + // https://fetch.spec.whatwg.org/#concept-body-clone + + // 1. Let « out1, out2 » be the result of teeing body’s stream. + const [out1, out2] = body.stream.tee() + const out2Clone = structuredClone(out2, { transfer: [out2] }) + // This, for whatever reasons, unrefs out2Clone which allows + // the process to exit by itself. + const [, finalClone] = out2Clone.tee() + + // 2. Set body’s stream to out1. + body.stream = out1 + + // 3. Return a body whose stream is out2 and other members are copied from body. + return { + stream: finalClone, + length: body.length, + source: body.source + } +} + +async function * consumeBody (body) { + if (body) { + if (isUint8Array(body)) { + yield body + } else { + const stream = body.stream + + if (util.isDisturbed(stream)) { + throw new TypeError('The body has already been consumed.') + } + + if (stream.locked) { + throw new TypeError('The stream is locked.') + } + + // Compat. + stream[kBodyUsed] = true + + yield * stream + } + } +} + +function throwIfAborted (state) { + if (state.aborted) { + throw new DOMException('The operation was aborted.', 'AbortError') + } +} + +function bodyMixinMethods (instance) { + const methods = { + blob () { + // The blob() method steps are to return the result of + // running consume body with this and the following step + // given a byte sequence bytes: return a Blob whose + // contents are bytes and whose type attribute is this’s + // MIME type. + return specConsumeBody(this, (bytes) => { + let mimeType = bodyMimeType(this) + + if (mimeType === 'failure') { + mimeType = '' + } else if (mimeType) { + mimeType = serializeAMimeType(mimeType) + } + + // Return a Blob whose contents are bytes and type attribute + // is mimeType. + return new Blob([bytes], { type: mimeType }) + }, instance) + }, + + arrayBuffer () { + // The arrayBuffer() method steps are to return the result + // of running consume body with this and the following step + // given a byte sequence bytes: return a new ArrayBuffer + // whose contents are bytes. + return specConsumeBody(this, (bytes) => { + return new Uint8Array(bytes).buffer + }, instance) + }, + + text () { + // The text() method steps are to return the result of running + // consume body with this and UTF-8 decode. + return specConsumeBody(this, utf8DecodeBytes, instance) + }, + + json () { + // The json() method steps are to return the result of running + // consume body with this and parse JSON from bytes. + return specConsumeBody(this, parseJSONFromBytes, instance) + }, + + async formData () { + webidl.brandCheck(this, instance) + + throwIfAborted(this[kState]) + + const contentType = this.headers.get('Content-Type') + + // If mimeType’s essence is "multipart/form-data", then: + if (/multipart\/form-data/.test(contentType)) { + const headers = {} + for (const [key, value] of this.headers) headers[key.toLowerCase()] = value + + const responseFormData = new FormData() + + let busboy + + try { + busboy = new Busboy({ + headers, + preservePath: true + }) + } catch (err) { + throw new DOMException(`${err}`, 'AbortError') + } + + busboy.on('field', (name, value) => { + responseFormData.append(name, value) + }) + busboy.on('file', (name, value, filename, encoding, mimeType) => { + const chunks = [] + + if (encoding === 'base64' || encoding.toLowerCase() === 'base64') { + let base64chunk = '' + + value.on('data', (chunk) => { + base64chunk += chunk.toString().replace(/[\r\n]/gm, '') + + const end = base64chunk.length - base64chunk.length % 4 + chunks.push(Buffer.from(base64chunk.slice(0, end), 'base64')) + + base64chunk = base64chunk.slice(end) + }) + value.on('end', () => { + chunks.push(Buffer.from(base64chunk, 'base64')) + responseFormData.append(name, new File(chunks, filename, { type: mimeType })) + }) + } else { + value.on('data', (chunk) => { + chunks.push(chunk) + }) + value.on('end', () => { + responseFormData.append(name, new File(chunks, filename, { type: mimeType })) + }) + } + }) + + const busboyResolve = new Promise((resolve, reject) => { + busboy.on('finish', resolve) + busboy.on('error', (err) => reject(new TypeError(err))) + }) + + if (this.body !== null) for await (const chunk of consumeBody(this[kState].body)) busboy.write(chunk) + busboy.end() + await busboyResolve + + return responseFormData + } else if (/application\/x-www-form-urlencoded/.test(contentType)) { + // Otherwise, if mimeType’s essence is "application/x-www-form-urlencoded", then: + + // 1. Let entries be the result of parsing bytes. + let entries + try { + let text = '' + // application/x-www-form-urlencoded parser will keep the BOM. + // https://url.spec.whatwg.org/#concept-urlencoded-parser + // Note that streaming decoder is stateful and cannot be reused + const streamingDecoder = new TextDecoder('utf-8', { ignoreBOM: true }) + + for await (const chunk of consumeBody(this[kState].body)) { + if (!isUint8Array(chunk)) { + throw new TypeError('Expected Uint8Array chunk') + } + text += streamingDecoder.decode(chunk, { stream: true }) + } + text += streamingDecoder.decode() + entries = new URLSearchParams(text) + } catch (err) { + // istanbul ignore next: Unclear when new URLSearchParams can fail on a string. + // 2. If entries is failure, then throw a TypeError. + throw Object.assign(new TypeError(), { cause: err }) + } + + // 3. Return a new FormData object whose entries are entries. + const formData = new FormData() + for (const [name, value] of entries) { + formData.append(name, value) + } + return formData + } else { + // Wait a tick before checking if the request has been aborted. + // Otherwise, a TypeError can be thrown when an AbortError should. + await Promise.resolve() + + throwIfAborted(this[kState]) + + // Otherwise, throw a TypeError. + throw webidl.errors.exception({ + header: `${instance.name}.formData`, + message: 'Could not parse content as FormData.' + }) + } + } + } + + return methods +} + +function mixinBody (prototype) { + Object.assign(prototype.prototype, bodyMixinMethods(prototype)) +} + +/** + * @see https://fetch.spec.whatwg.org/#concept-body-consume-body + * @param {Response|Request} object + * @param {(value: unknown) => unknown} convertBytesToJSValue + * @param {Response|Request} instance + */ +async function specConsumeBody (object, convertBytesToJSValue, instance) { + webidl.brandCheck(object, instance) + + throwIfAborted(object[kState]) + + // 1. If object is unusable, then return a promise rejected + // with a TypeError. + if (bodyUnusable(object[kState].body)) { + throw new TypeError('Body is unusable') + } + + // 2. Let promise be a new promise. + const promise = createDeferredPromise() + + // 3. Let errorSteps given error be to reject promise with error. + const errorSteps = (error) => promise.reject(error) + + // 4. Let successSteps given a byte sequence data be to resolve + // promise with the result of running convertBytesToJSValue + // with data. If that threw an exception, then run errorSteps + // with that exception. + const successSteps = (data) => { + try { + promise.resolve(convertBytesToJSValue(data)) + } catch (e) { + errorSteps(e) + } + } + + // 5. If object’s body is null, then run successSteps with an + // empty byte sequence. + if (object[kState].body == null) { + successSteps(new Uint8Array()) + return promise.promise + } + + // 6. Otherwise, fully read object’s body given successSteps, + // errorSteps, and object’s relevant global object. + await fullyReadBody(object[kState].body, successSteps, errorSteps) + + // 7. Return promise. + return promise.promise +} + +// https://fetch.spec.whatwg.org/#body-unusable +function bodyUnusable (body) { + // An object including the Body interface mixin is + // said to be unusable if its body is non-null and + // its body’s stream is disturbed or locked. + return body != null && (body.stream.locked || util.isDisturbed(body.stream)) +} + +/** + * @see https://encoding.spec.whatwg.org/#utf-8-decode + * @param {Buffer} buffer + */ +function utf8DecodeBytes (buffer) { + if (buffer.length === 0) { + return '' + } + + // 1. Let buffer be the result of peeking three bytes from + // ioQueue, converted to a byte sequence. + + // 2. If buffer is 0xEF 0xBB 0xBF, then read three + // bytes from ioQueue. (Do nothing with those bytes.) + if (buffer[0] === 0xEF && buffer[1] === 0xBB && buffer[2] === 0xBF) { + buffer = buffer.subarray(3) + } + + // 3. Process a queue with an instance of UTF-8’s + // decoder, ioQueue, output, and "replacement". + const output = textDecoder.decode(buffer) + + // 4. Return output. + return output +} + +/** + * @see https://infra.spec.whatwg.org/#parse-json-bytes-to-a-javascript-value + * @param {Uint8Array} bytes + */ +function parseJSONFromBytes (bytes) { + return JSON.parse(utf8DecodeBytes(bytes)) +} + +/** + * @see https://fetch.spec.whatwg.org/#concept-body-mime-type + * @param {import('./response').Response|import('./request').Request} object + */ +function bodyMimeType (object) { + const { headersList } = object[kState] + const contentType = headersList.get('content-type') + + if (contentType === null) { + return 'failure' + } + + return parseMIMEType(contentType) +} + +module.exports = { + extractBody, + safelyExtractBody, + cloneBody, + mixinBody +} + + +/***/ }), + +/***/ 1037: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { MessageChannel, receiveMessageOnPort } = __nccwpck_require__(1267) + +const corsSafeListedMethods = ['GET', 'HEAD', 'POST'] +const corsSafeListedMethodsSet = new Set(corsSafeListedMethods) + +const nullBodyStatus = [101, 204, 205, 304] + +const redirectStatus = [301, 302, 303, 307, 308] +const redirectStatusSet = new Set(redirectStatus) + +// https://fetch.spec.whatwg.org/#block-bad-port +const badPorts = [ + '1', '7', '9', '11', '13', '15', '17', '19', '20', '21', '22', '23', '25', '37', '42', '43', '53', '69', '77', '79', + '87', '95', '101', '102', '103', '104', '109', '110', '111', '113', '115', '117', '119', '123', '135', '137', + '139', '143', '161', '179', '389', '427', '465', '512', '513', '514', '515', '526', '530', '531', '532', + '540', '548', '554', '556', '563', '587', '601', '636', '989', '990', '993', '995', '1719', '1720', '1723', + '2049', '3659', '4045', '5060', '5061', '6000', '6566', '6665', '6666', '6667', '6668', '6669', '6697', + '10080' +] + +const badPortsSet = new Set(badPorts) + +// https://w3c.github.io/webappsec-referrer-policy/#referrer-policies +const referrerPolicy = [ + '', + 'no-referrer', + 'no-referrer-when-downgrade', + 'same-origin', + 'origin', + 'strict-origin', + 'origin-when-cross-origin', + 'strict-origin-when-cross-origin', + 'unsafe-url' +] +const referrerPolicySet = new Set(referrerPolicy) + +const requestRedirect = ['follow', 'manual', 'error'] + +const safeMethods = ['GET', 'HEAD', 'OPTIONS', 'TRACE'] +const safeMethodsSet = new Set(safeMethods) + +const requestMode = ['navigate', 'same-origin', 'no-cors', 'cors'] + +const requestCredentials = ['omit', 'same-origin', 'include'] + +const requestCache = [ + 'default', + 'no-store', + 'reload', + 'no-cache', + 'force-cache', + 'only-if-cached' +] + +// https://fetch.spec.whatwg.org/#request-body-header-name +const requestBodyHeader = [ + 'content-encoding', + 'content-language', + 'content-location', + 'content-type', + // See https://github.com/nodejs/undici/issues/2021 + // 'Content-Length' is a forbidden header name, which is typically + // removed in the Headers implementation. However, undici doesn't + // filter out headers, so we add it here. + 'content-length' +] + +// https://fetch.spec.whatwg.org/#enumdef-requestduplex +const requestDuplex = [ + 'half' +] + +// http://fetch.spec.whatwg.org/#forbidden-method +const forbiddenMethods = ['CONNECT', 'TRACE', 'TRACK'] +const forbiddenMethodsSet = new Set(forbiddenMethods) + +const subresource = [ + 'audio', + 'audioworklet', + 'font', + 'image', + 'manifest', + 'paintworklet', + 'script', + 'style', + 'track', + 'video', + 'xslt', + '' +] +const subresourceSet = new Set(subresource) + +/** @type {globalThis['DOMException']} */ +const DOMException = globalThis.DOMException ?? (() => { + // DOMException was only made a global in Node v17.0.0, + // but fetch supports >= v16.8. + try { + atob('~') + } catch (err) { + return Object.getPrototypeOf(err).constructor + } +})() + +let channel + +/** @type {globalThis['structuredClone']} */ +const structuredClone = + globalThis.structuredClone ?? + // https://github.com/nodejs/node/blob/b27ae24dcc4251bad726d9d84baf678d1f707fed/lib/internal/structured_clone.js + // structuredClone was added in v17.0.0, but fetch supports v16.8 + function structuredClone (value, options = undefined) { + if (arguments.length === 0) { + throw new TypeError('missing argument') + } + + if (!channel) { + channel = new MessageChannel() + } + channel.port1.unref() + channel.port2.unref() + channel.port1.postMessage(value, options?.transfer) + return receiveMessageOnPort(channel.port2).message + } + +module.exports = { + DOMException, + structuredClone, + subresource, + forbiddenMethods, + requestBodyHeader, + referrerPolicy, + requestRedirect, + requestMode, + requestCredentials, + requestCache, + redirectStatus, + corsSafeListedMethods, + nullBodyStatus, + safeMethods, + badPorts, + requestDuplex, + subresourceSet, + badPortsSet, + redirectStatusSet, + corsSafeListedMethodsSet, + safeMethodsSet, + forbiddenMethodsSet, + referrerPolicySet +} + + +/***/ }), + +/***/ 685: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +const assert = __nccwpck_require__(9491) +const { atob } = __nccwpck_require__(4300) +const { isomorphicDecode } = __nccwpck_require__(2538) + +const encoder = new TextEncoder() + +/** + * @see https://mimesniff.spec.whatwg.org/#http-token-code-point + */ +const HTTP_TOKEN_CODEPOINTS = /^[!#$%&'*+-.^_|~A-Za-z0-9]+$/ +const HTTP_WHITESPACE_REGEX = /(\u000A|\u000D|\u0009|\u0020)/ // eslint-disable-line +/** + * @see https://mimesniff.spec.whatwg.org/#http-quoted-string-token-code-point + */ +const HTTP_QUOTED_STRING_TOKENS = /[\u0009|\u0020-\u007E|\u0080-\u00FF]/ // eslint-disable-line + +// https://fetch.spec.whatwg.org/#data-url-processor +/** @param {URL} dataURL */ +function dataURLProcessor (dataURL) { + // 1. Assert: dataURL’s scheme is "data". + assert(dataURL.protocol === 'data:') + + // 2. Let input be the result of running the URL + // serializer on dataURL with exclude fragment + // set to true. + let input = URLSerializer(dataURL, true) + + // 3. Remove the leading "data:" string from input. + input = input.slice(5) + + // 4. Let position point at the start of input. + const position = { position: 0 } + + // 5. Let mimeType be the result of collecting a + // sequence of code points that are not equal + // to U+002C (,), given position. + let mimeType = collectASequenceOfCodePointsFast( + ',', + input, + position + ) + + // 6. Strip leading and trailing ASCII whitespace + // from mimeType. + // Undici implementation note: we need to store the + // length because if the mimetype has spaces removed, + // the wrong amount will be sliced from the input in + // step #9 + const mimeTypeLength = mimeType.length + mimeType = removeASCIIWhitespace(mimeType, true, true) + + // 7. If position is past the end of input, then + // return failure + if (position.position >= input.length) { + return 'failure' + } + + // 8. Advance position by 1. + position.position++ + + // 9. Let encodedBody be the remainder of input. + const encodedBody = input.slice(mimeTypeLength + 1) + + // 10. Let body be the percent-decoding of encodedBody. + let body = stringPercentDecode(encodedBody) + + // 11. If mimeType ends with U+003B (;), followed by + // zero or more U+0020 SPACE, followed by an ASCII + // case-insensitive match for "base64", then: + if (/;(\u0020){0,}base64$/i.test(mimeType)) { + // 1. Let stringBody be the isomorphic decode of body. + const stringBody = isomorphicDecode(body) + + // 2. Set body to the forgiving-base64 decode of + // stringBody. + body = forgivingBase64(stringBody) + + // 3. If body is failure, then return failure. + if (body === 'failure') { + return 'failure' + } + + // 4. Remove the last 6 code points from mimeType. + mimeType = mimeType.slice(0, -6) + + // 5. Remove trailing U+0020 SPACE code points from mimeType, + // if any. + mimeType = mimeType.replace(/(\u0020)+$/, '') + + // 6. Remove the last U+003B (;) code point from mimeType. + mimeType = mimeType.slice(0, -1) + } + + // 12. If mimeType starts with U+003B (;), then prepend + // "text/plain" to mimeType. + if (mimeType.startsWith(';')) { + mimeType = 'text/plain' + mimeType + } + + // 13. Let mimeTypeRecord be the result of parsing + // mimeType. + let mimeTypeRecord = parseMIMEType(mimeType) + + // 14. If mimeTypeRecord is failure, then set + // mimeTypeRecord to text/plain;charset=US-ASCII. + if (mimeTypeRecord === 'failure') { + mimeTypeRecord = parseMIMEType('text/plain;charset=US-ASCII') + } + + // 15. Return a new data: URL struct whose MIME + // type is mimeTypeRecord and body is body. + // https://fetch.spec.whatwg.org/#data-url-struct + return { mimeType: mimeTypeRecord, body } +} + +// https://url.spec.whatwg.org/#concept-url-serializer +/** + * @param {URL} url + * @param {boolean} excludeFragment + */ +function URLSerializer (url, excludeFragment = false) { + const href = url.href + + if (!excludeFragment) { + return href + } + + const hash = href.lastIndexOf('#') + if (hash === -1) { + return href + } + return href.slice(0, hash) +} + +// https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points +/** + * @param {(char: string) => boolean} condition + * @param {string} input + * @param {{ position: number }} position + */ +function collectASequenceOfCodePoints (condition, input, position) { + // 1. Let result be the empty string. + let result = '' + + // 2. While position doesn’t point past the end of input and the + // code point at position within input meets the condition condition: + while (position.position < input.length && condition(input[position.position])) { + // 1. Append that code point to the end of result. + result += input[position.position] + + // 2. Advance position by 1. + position.position++ + } + + // 3. Return result. + return result +} + +/** + * A faster collectASequenceOfCodePoints that only works when comparing a single character. + * @param {string} char + * @param {string} input + * @param {{ position: number }} position + */ +function collectASequenceOfCodePointsFast (char, input, position) { + const idx = input.indexOf(char, position.position) + const start = position.position + + if (idx === -1) { + position.position = input.length + return input.slice(start) + } + + position.position = idx + return input.slice(start, position.position) +} + +// https://url.spec.whatwg.org/#string-percent-decode +/** @param {string} input */ +function stringPercentDecode (input) { + // 1. Let bytes be the UTF-8 encoding of input. + const bytes = encoder.encode(input) + + // 2. Return the percent-decoding of bytes. + return percentDecode(bytes) +} + +// https://url.spec.whatwg.org/#percent-decode +/** @param {Uint8Array} input */ +function percentDecode (input) { + // 1. Let output be an empty byte sequence. + /** @type {number[]} */ + const output = [] + + // 2. For each byte byte in input: + for (let i = 0; i < input.length; i++) { + const byte = input[i] + + // 1. If byte is not 0x25 (%), then append byte to output. + if (byte !== 0x25) { + output.push(byte) + + // 2. Otherwise, if byte is 0x25 (%) and the next two bytes + // after byte in input are not in the ranges + // 0x30 (0) to 0x39 (9), 0x41 (A) to 0x46 (F), + // and 0x61 (a) to 0x66 (f), all inclusive, append byte + // to output. + } else if ( + byte === 0x25 && + !/^[0-9A-Fa-f]{2}$/i.test(String.fromCharCode(input[i + 1], input[i + 2])) + ) { + output.push(0x25) + + // 3. Otherwise: + } else { + // 1. Let bytePoint be the two bytes after byte in input, + // decoded, and then interpreted as hexadecimal number. + const nextTwoBytes = String.fromCharCode(input[i + 1], input[i + 2]) + const bytePoint = Number.parseInt(nextTwoBytes, 16) + + // 2. Append a byte whose value is bytePoint to output. + output.push(bytePoint) + + // 3. Skip the next two bytes in input. + i += 2 + } + } + + // 3. Return output. + return Uint8Array.from(output) +} + +// https://mimesniff.spec.whatwg.org/#parse-a-mime-type +/** @param {string} input */ +function parseMIMEType (input) { + // 1. Remove any leading and trailing HTTP whitespace + // from input. + input = removeHTTPWhitespace(input, true, true) + + // 2. Let position be a position variable for input, + // initially pointing at the start of input. + const position = { position: 0 } + + // 3. Let type be the result of collecting a sequence + // of code points that are not U+002F (/) from + // input, given position. + const type = collectASequenceOfCodePointsFast( + '/', + input, + position + ) + + // 4. If type is the empty string or does not solely + // contain HTTP token code points, then return failure. + // https://mimesniff.spec.whatwg.org/#http-token-code-point + if (type.length === 0 || !HTTP_TOKEN_CODEPOINTS.test(type)) { + return 'failure' + } + + // 5. If position is past the end of input, then return + // failure + if (position.position > input.length) { + return 'failure' + } + + // 6. Advance position by 1. (This skips past U+002F (/).) + position.position++ + + // 7. Let subtype be the result of collecting a sequence of + // code points that are not U+003B (;) from input, given + // position. + let subtype = collectASequenceOfCodePointsFast( + ';', + input, + position + ) + + // 8. Remove any trailing HTTP whitespace from subtype. + subtype = removeHTTPWhitespace(subtype, false, true) + + // 9. If subtype is the empty string or does not solely + // contain HTTP token code points, then return failure. + if (subtype.length === 0 || !HTTP_TOKEN_CODEPOINTS.test(subtype)) { + return 'failure' + } + + const typeLowercase = type.toLowerCase() + const subtypeLowercase = subtype.toLowerCase() + + // 10. Let mimeType be a new MIME type record whose type + // is type, in ASCII lowercase, and subtype is subtype, + // in ASCII lowercase. + // https://mimesniff.spec.whatwg.org/#mime-type + const mimeType = { + type: typeLowercase, + subtype: subtypeLowercase, + /** @type {Map} */ + parameters: new Map(), + // https://mimesniff.spec.whatwg.org/#mime-type-essence + essence: `${typeLowercase}/${subtypeLowercase}` + } + + // 11. While position is not past the end of input: + while (position.position < input.length) { + // 1. Advance position by 1. (This skips past U+003B (;).) + position.position++ + + // 2. Collect a sequence of code points that are HTTP + // whitespace from input given position. + collectASequenceOfCodePoints( + // https://fetch.spec.whatwg.org/#http-whitespace + char => HTTP_WHITESPACE_REGEX.test(char), + input, + position + ) + + // 3. Let parameterName be the result of collecting a + // sequence of code points that are not U+003B (;) + // or U+003D (=) from input, given position. + let parameterName = collectASequenceOfCodePoints( + (char) => char !== ';' && char !== '=', + input, + position + ) + + // 4. Set parameterName to parameterName, in ASCII + // lowercase. + parameterName = parameterName.toLowerCase() + + // 5. If position is not past the end of input, then: + if (position.position < input.length) { + // 1. If the code point at position within input is + // U+003B (;), then continue. + if (input[position.position] === ';') { + continue + } + + // 2. Advance position by 1. (This skips past U+003D (=).) + position.position++ + } + + // 6. If position is past the end of input, then break. + if (position.position > input.length) { + break + } + + // 7. Let parameterValue be null. + let parameterValue = null + + // 8. If the code point at position within input is + // U+0022 ("), then: + if (input[position.position] === '"') { + // 1. Set parameterValue to the result of collecting + // an HTTP quoted string from input, given position + // and the extract-value flag. + parameterValue = collectAnHTTPQuotedString(input, position, true) + + // 2. Collect a sequence of code points that are not + // U+003B (;) from input, given position. + collectASequenceOfCodePointsFast( + ';', + input, + position + ) + + // 9. Otherwise: + } else { + // 1. Set parameterValue to the result of collecting + // a sequence of code points that are not U+003B (;) + // from input, given position. + parameterValue = collectASequenceOfCodePointsFast( + ';', + input, + position + ) + + // 2. Remove any trailing HTTP whitespace from parameterValue. + parameterValue = removeHTTPWhitespace(parameterValue, false, true) + + // 3. If parameterValue is the empty string, then continue. + if (parameterValue.length === 0) { + continue + } + } + + // 10. If all of the following are true + // - parameterName is not the empty string + // - parameterName solely contains HTTP token code points + // - parameterValue solely contains HTTP quoted-string token code points + // - mimeType’s parameters[parameterName] does not exist + // then set mimeType’s parameters[parameterName] to parameterValue. + if ( + parameterName.length !== 0 && + HTTP_TOKEN_CODEPOINTS.test(parameterName) && + (parameterValue.length === 0 || HTTP_QUOTED_STRING_TOKENS.test(parameterValue)) && + !mimeType.parameters.has(parameterName) + ) { + mimeType.parameters.set(parameterName, parameterValue) + } + } + + // 12. Return mimeType. + return mimeType +} + +// https://infra.spec.whatwg.org/#forgiving-base64-decode +/** @param {string} data */ +function forgivingBase64 (data) { + // 1. Remove all ASCII whitespace from data. + data = data.replace(/[\u0009\u000A\u000C\u000D\u0020]/g, '') // eslint-disable-line + + // 2. If data’s code point length divides by 4 leaving + // no remainder, then: + if (data.length % 4 === 0) { + // 1. If data ends with one or two U+003D (=) code points, + // then remove them from data. + data = data.replace(/=?=$/, '') + } + + // 3. If data’s code point length divides by 4 leaving + // a remainder of 1, then return failure. + if (data.length % 4 === 1) { + return 'failure' + } + + // 4. If data contains a code point that is not one of + // U+002B (+) + // U+002F (/) + // ASCII alphanumeric + // then return failure. + if (/[^+/0-9A-Za-z]/.test(data)) { + return 'failure' + } + + const binary = atob(data) + const bytes = new Uint8Array(binary.length) + + for (let byte = 0; byte < binary.length; byte++) { + bytes[byte] = binary.charCodeAt(byte) + } + + return bytes +} + +// https://fetch.spec.whatwg.org/#collect-an-http-quoted-string +// tests: https://fetch.spec.whatwg.org/#example-http-quoted-string +/** + * @param {string} input + * @param {{ position: number }} position + * @param {boolean?} extractValue + */ +function collectAnHTTPQuotedString (input, position, extractValue) { + // 1. Let positionStart be position. + const positionStart = position.position + + // 2. Let value be the empty string. + let value = '' + + // 3. Assert: the code point at position within input + // is U+0022 ("). + assert(input[position.position] === '"') + + // 4. Advance position by 1. + position.position++ + + // 5. While true: + while (true) { + // 1. Append the result of collecting a sequence of code points + // that are not U+0022 (") or U+005C (\) from input, given + // position, to value. + value += collectASequenceOfCodePoints( + (char) => char !== '"' && char !== '\\', + input, + position + ) + + // 2. If position is past the end of input, then break. + if (position.position >= input.length) { + break + } + + // 3. Let quoteOrBackslash be the code point at position within + // input. + const quoteOrBackslash = input[position.position] + + // 4. Advance position by 1. + position.position++ + + // 5. If quoteOrBackslash is U+005C (\), then: + if (quoteOrBackslash === '\\') { + // 1. If position is past the end of input, then append + // U+005C (\) to value and break. + if (position.position >= input.length) { + value += '\\' + break + } + + // 2. Append the code point at position within input to value. + value += input[position.position] + + // 3. Advance position by 1. + position.position++ + + // 6. Otherwise: + } else { + // 1. Assert: quoteOrBackslash is U+0022 ("). + assert(quoteOrBackslash === '"') + + // 2. Break. + break + } + } + + // 6. If the extract-value flag is set, then return value. + if (extractValue) { + return value + } + + // 7. Return the code points from positionStart to position, + // inclusive, within input. + return input.slice(positionStart, position.position) +} + +/** + * @see https://mimesniff.spec.whatwg.org/#serialize-a-mime-type + */ +function serializeAMimeType (mimeType) { + assert(mimeType !== 'failure') + const { parameters, essence } = mimeType + + // 1. Let serialization be the concatenation of mimeType’s + // type, U+002F (/), and mimeType’s subtype. + let serialization = essence + + // 2. For each name → value of mimeType’s parameters: + for (let [name, value] of parameters.entries()) { + // 1. Append U+003B (;) to serialization. + serialization += ';' + + // 2. Append name to serialization. + serialization += name + + // 3. Append U+003D (=) to serialization. + serialization += '=' + + // 4. If value does not solely contain HTTP token code + // points or value is the empty string, then: + if (!HTTP_TOKEN_CODEPOINTS.test(value)) { + // 1. Precede each occurence of U+0022 (") or + // U+005C (\) in value with U+005C (\). + value = value.replace(/(\\|")/g, '\\$1') + + // 2. Prepend U+0022 (") to value. + value = '"' + value + + // 3. Append U+0022 (") to value. + value += '"' + } + + // 5. Append value to serialization. + serialization += value + } + + // 3. Return serialization. + return serialization +} + +/** + * @see https://fetch.spec.whatwg.org/#http-whitespace + * @param {string} char + */ +function isHTTPWhiteSpace (char) { + return char === '\r' || char === '\n' || char === '\t' || char === ' ' +} + +/** + * @see https://fetch.spec.whatwg.org/#http-whitespace + * @param {string} str + */ +function removeHTTPWhitespace (str, leading = true, trailing = true) { + let lead = 0 + let trail = str.length - 1 + + if (leading) { + for (; lead < str.length && isHTTPWhiteSpace(str[lead]); lead++); + } + + if (trailing) { + for (; trail > 0 && isHTTPWhiteSpace(str[trail]); trail--); + } + + return str.slice(lead, trail + 1) +} + +/** + * @see https://infra.spec.whatwg.org/#ascii-whitespace + * @param {string} char + */ +function isASCIIWhitespace (char) { + return char === '\r' || char === '\n' || char === '\t' || char === '\f' || char === ' ' +} + +/** + * @see https://infra.spec.whatwg.org/#strip-leading-and-trailing-ascii-whitespace + */ +function removeASCIIWhitespace (str, leading = true, trailing = true) { + let lead = 0 + let trail = str.length - 1 + + if (leading) { + for (; lead < str.length && isASCIIWhitespace(str[lead]); lead++); + } + + if (trailing) { + for (; trail > 0 && isASCIIWhitespace(str[trail]); trail--); + } + + return str.slice(lead, trail + 1) +} + +module.exports = { + dataURLProcessor, + URLSerializer, + collectASequenceOfCodePoints, + collectASequenceOfCodePointsFast, + stringPercentDecode, + parseMIMEType, + collectAnHTTPQuotedString, + serializeAMimeType +} + + +/***/ }), + +/***/ 8511: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { Blob, File: NativeFile } = __nccwpck_require__(4300) +const { types } = __nccwpck_require__(3837) +const { kState } = __nccwpck_require__(5861) +const { isBlobLike } = __nccwpck_require__(2538) +const { webidl } = __nccwpck_require__(1744) +const { parseMIMEType, serializeAMimeType } = __nccwpck_require__(685) +const { kEnumerableProperty } = __nccwpck_require__(3983) +const encoder = new TextEncoder() + +class File extends Blob { + constructor (fileBits, fileName, options = {}) { + // The File constructor is invoked with two or three parameters, depending + // on whether the optional dictionary parameter is used. When the File() + // constructor is invoked, user agents must run the following steps: + webidl.argumentLengthCheck(arguments, 2, { header: 'File constructor' }) + + fileBits = webidl.converters['sequence'](fileBits) + fileName = webidl.converters.USVString(fileName) + options = webidl.converters.FilePropertyBag(options) + + // 1. Let bytes be the result of processing blob parts given fileBits and + // options. + // Note: Blob handles this for us + + // 2. Let n be the fileName argument to the constructor. + const n = fileName + + // 3. Process FilePropertyBag dictionary argument by running the following + // substeps: + + // 1. If the type member is provided and is not the empty string, let t + // be set to the type dictionary member. If t contains any characters + // outside the range U+0020 to U+007E, then set t to the empty string + // and return from these substeps. + // 2. Convert every character in t to ASCII lowercase. + let t = options.type + let d + + // eslint-disable-next-line no-labels + substep: { + if (t) { + t = parseMIMEType(t) + + if (t === 'failure') { + t = '' + // eslint-disable-next-line no-labels + break substep + } + + t = serializeAMimeType(t).toLowerCase() + } + + // 3. If the lastModified member is provided, let d be set to the + // lastModified dictionary member. If it is not provided, set d to the + // current date and time represented as the number of milliseconds since + // the Unix Epoch (which is the equivalent of Date.now() [ECMA-262]). + d = options.lastModified + } + + // 4. Return a new File object F such that: + // F refers to the bytes byte sequence. + // F.size is set to the number of total bytes in bytes. + // F.name is set to n. + // F.type is set to t. + // F.lastModified is set to d. + + super(processBlobParts(fileBits, options), { type: t }) + this[kState] = { + name: n, + lastModified: d, + type: t + } + } + + get name () { + webidl.brandCheck(this, File) + + return this[kState].name + } + + get lastModified () { + webidl.brandCheck(this, File) + + return this[kState].lastModified + } + + get type () { + webidl.brandCheck(this, File) + + return this[kState].type + } +} + +class FileLike { + constructor (blobLike, fileName, options = {}) { + // TODO: argument idl type check + + // The File constructor is invoked with two or three parameters, depending + // on whether the optional dictionary parameter is used. When the File() + // constructor is invoked, user agents must run the following steps: + + // 1. Let bytes be the result of processing blob parts given fileBits and + // options. + + // 2. Let n be the fileName argument to the constructor. + const n = fileName + + // 3. Process FilePropertyBag dictionary argument by running the following + // substeps: + + // 1. If the type member is provided and is not the empty string, let t + // be set to the type dictionary member. If t contains any characters + // outside the range U+0020 to U+007E, then set t to the empty string + // and return from these substeps. + // TODO + const t = options.type + + // 2. Convert every character in t to ASCII lowercase. + // TODO + + // 3. If the lastModified member is provided, let d be set to the + // lastModified dictionary member. If it is not provided, set d to the + // current date and time represented as the number of milliseconds since + // the Unix Epoch (which is the equivalent of Date.now() [ECMA-262]). + const d = options.lastModified ?? Date.now() + + // 4. Return a new File object F such that: + // F refers to the bytes byte sequence. + // F.size is set to the number of total bytes in bytes. + // F.name is set to n. + // F.type is set to t. + // F.lastModified is set to d. + + this[kState] = { + blobLike, + name: n, + type: t, + lastModified: d + } + } + + stream (...args) { + webidl.brandCheck(this, FileLike) + + return this[kState].blobLike.stream(...args) + } + + arrayBuffer (...args) { + webidl.brandCheck(this, FileLike) + + return this[kState].blobLike.arrayBuffer(...args) + } + + slice (...args) { + webidl.brandCheck(this, FileLike) + + return this[kState].blobLike.slice(...args) + } + + text (...args) { + webidl.brandCheck(this, FileLike) + + return this[kState].blobLike.text(...args) + } + + get size () { + webidl.brandCheck(this, FileLike) + + return this[kState].blobLike.size + } + + get type () { + webidl.brandCheck(this, FileLike) + + return this[kState].blobLike.type + } + + get name () { + webidl.brandCheck(this, FileLike) + + return this[kState].name + } + + get lastModified () { + webidl.brandCheck(this, FileLike) + + return this[kState].lastModified + } + + get [Symbol.toStringTag] () { + return 'File' + } +} + +Object.defineProperties(File.prototype, { + [Symbol.toStringTag]: { + value: 'File', + configurable: true + }, + name: kEnumerableProperty, + lastModified: kEnumerableProperty +}) + +webidl.converters.Blob = webidl.interfaceConverter(Blob) + +webidl.converters.BlobPart = function (V, opts) { + if (webidl.util.Type(V) === 'Object') { + if (isBlobLike(V)) { + return webidl.converters.Blob(V, { strict: false }) + } + + if ( + ArrayBuffer.isView(V) || + types.isAnyArrayBuffer(V) + ) { + return webidl.converters.BufferSource(V, opts) + } + } + + return webidl.converters.USVString(V, opts) +} + +webidl.converters['sequence'] = webidl.sequenceConverter( + webidl.converters.BlobPart +) + +// https://www.w3.org/TR/FileAPI/#dfn-FilePropertyBag +webidl.converters.FilePropertyBag = webidl.dictionaryConverter([ + { + key: 'lastModified', + converter: webidl.converters['long long'], + get defaultValue () { + return Date.now() + } + }, + { + key: 'type', + converter: webidl.converters.DOMString, + defaultValue: '' + }, + { + key: 'endings', + converter: (value) => { + value = webidl.converters.DOMString(value) + value = value.toLowerCase() + + if (value !== 'native') { + value = 'transparent' + } + + return value + }, + defaultValue: 'transparent' + } +]) + +/** + * @see https://www.w3.org/TR/FileAPI/#process-blob-parts + * @param {(NodeJS.TypedArray|Blob|string)[]} parts + * @param {{ type: string, endings: string }} options + */ +function processBlobParts (parts, options) { + // 1. Let bytes be an empty sequence of bytes. + /** @type {NodeJS.TypedArray[]} */ + const bytes = [] + + // 2. For each element in parts: + for (const element of parts) { + // 1. If element is a USVString, run the following substeps: + if (typeof element === 'string') { + // 1. Let s be element. + let s = element + + // 2. If the endings member of options is "native", set s + // to the result of converting line endings to native + // of element. + if (options.endings === 'native') { + s = convertLineEndingsNative(s) + } + + // 3. Append the result of UTF-8 encoding s to bytes. + bytes.push(encoder.encode(s)) + } else if ( + types.isAnyArrayBuffer(element) || + types.isTypedArray(element) + ) { + // 2. If element is a BufferSource, get a copy of the + // bytes held by the buffer source, and append those + // bytes to bytes. + if (!element.buffer) { // ArrayBuffer + bytes.push(new Uint8Array(element)) + } else { + bytes.push( + new Uint8Array(element.buffer, element.byteOffset, element.byteLength) + ) + } + } else if (isBlobLike(element)) { + // 3. If element is a Blob, append the bytes it represents + // to bytes. + bytes.push(element) + } + } + + // 3. Return bytes. + return bytes +} + +/** + * @see https://www.w3.org/TR/FileAPI/#convert-line-endings-to-native + * @param {string} s + */ +function convertLineEndingsNative (s) { + // 1. Let native line ending be be the code point U+000A LF. + let nativeLineEnding = '\n' + + // 2. If the underlying platform’s conventions are to + // represent newlines as a carriage return and line feed + // sequence, set native line ending to the code point + // U+000D CR followed by the code point U+000A LF. + if (process.platform === 'win32') { + nativeLineEnding = '\r\n' + } + + return s.replace(/\r?\n/g, nativeLineEnding) +} + +// If this function is moved to ./util.js, some tools (such as +// rollup) will warn about circular dependencies. See: +// https://github.com/nodejs/undici/issues/1629 +function isFileLike (object) { + return ( + (NativeFile && object instanceof NativeFile) || + object instanceof File || ( + object && + (typeof object.stream === 'function' || + typeof object.arrayBuffer === 'function') && + object[Symbol.toStringTag] === 'File' + ) + ) +} + +module.exports = { File, FileLike, isFileLike } + + +/***/ }), + +/***/ 2015: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { isBlobLike, toUSVString, makeIterator } = __nccwpck_require__(2538) +const { kState } = __nccwpck_require__(5861) +const { File: UndiciFile, FileLike, isFileLike } = __nccwpck_require__(8511) +const { webidl } = __nccwpck_require__(1744) +const { Blob, File: NativeFile } = __nccwpck_require__(4300) + +/** @type {globalThis['File']} */ +const File = NativeFile ?? UndiciFile + +// https://xhr.spec.whatwg.org/#formdata +class FormData { + constructor (form) { + if (form !== undefined) { + throw webidl.errors.conversionFailed({ + prefix: 'FormData constructor', + argument: 'Argument 1', + types: ['undefined'] + }) + } + + this[kState] = [] + } + + append (name, value, filename = undefined) { + webidl.brandCheck(this, FormData) + + webidl.argumentLengthCheck(arguments, 2, { header: 'FormData.append' }) + + if (arguments.length === 3 && !isBlobLike(value)) { + throw new TypeError( + "Failed to execute 'append' on 'FormData': parameter 2 is not of type 'Blob'" + ) + } + + // 1. Let value be value if given; otherwise blobValue. + + name = webidl.converters.USVString(name) + value = isBlobLike(value) + ? webidl.converters.Blob(value, { strict: false }) + : webidl.converters.USVString(value) + filename = arguments.length === 3 + ? webidl.converters.USVString(filename) + : undefined + + // 2. Let entry be the result of creating an entry with + // name, value, and filename if given. + const entry = makeEntry(name, value, filename) + + // 3. Append entry to this’s entry list. + this[kState].push(entry) + } + + delete (name) { + webidl.brandCheck(this, FormData) + + webidl.argumentLengthCheck(arguments, 1, { header: 'FormData.delete' }) + + name = webidl.converters.USVString(name) + + // The delete(name) method steps are to remove all entries whose name + // is name from this’s entry list. + this[kState] = this[kState].filter(entry => entry.name !== name) + } + + get (name) { + webidl.brandCheck(this, FormData) + + webidl.argumentLengthCheck(arguments, 1, { header: 'FormData.get' }) + + name = webidl.converters.USVString(name) + + // 1. If there is no entry whose name is name in this’s entry list, + // then return null. + const idx = this[kState].findIndex((entry) => entry.name === name) + if (idx === -1) { + return null + } + + // 2. Return the value of the first entry whose name is name from + // this’s entry list. + return this[kState][idx].value + } + + getAll (name) { + webidl.brandCheck(this, FormData) + + webidl.argumentLengthCheck(arguments, 1, { header: 'FormData.getAll' }) + + name = webidl.converters.USVString(name) + + // 1. If there is no entry whose name is name in this’s entry list, + // then return the empty list. + // 2. Return the values of all entries whose name is name, in order, + // from this’s entry list. + return this[kState] + .filter((entry) => entry.name === name) + .map((entry) => entry.value) + } + + has (name) { + webidl.brandCheck(this, FormData) + + webidl.argumentLengthCheck(arguments, 1, { header: 'FormData.has' }) + + name = webidl.converters.USVString(name) + + // The has(name) method steps are to return true if there is an entry + // whose name is name in this’s entry list; otherwise false. + return this[kState].findIndex((entry) => entry.name === name) !== -1 + } + + set (name, value, filename = undefined) { + webidl.brandCheck(this, FormData) + + webidl.argumentLengthCheck(arguments, 2, { header: 'FormData.set' }) + + if (arguments.length === 3 && !isBlobLike(value)) { + throw new TypeError( + "Failed to execute 'set' on 'FormData': parameter 2 is not of type 'Blob'" + ) + } + + // The set(name, value) and set(name, blobValue, filename) method steps + // are: + + // 1. Let value be value if given; otherwise blobValue. + + name = webidl.converters.USVString(name) + value = isBlobLike(value) + ? webidl.converters.Blob(value, { strict: false }) + : webidl.converters.USVString(value) + filename = arguments.length === 3 + ? toUSVString(filename) + : undefined + + // 2. Let entry be the result of creating an entry with name, value, and + // filename if given. + const entry = makeEntry(name, value, filename) + + // 3. If there are entries in this’s entry list whose name is name, then + // replace the first such entry with entry and remove the others. + const idx = this[kState].findIndex((entry) => entry.name === name) + if (idx !== -1) { + this[kState] = [ + ...this[kState].slice(0, idx), + entry, + ...this[kState].slice(idx + 1).filter((entry) => entry.name !== name) + ] + } else { + // 4. Otherwise, append entry to this’s entry list. + this[kState].push(entry) + } + } + + entries () { + webidl.brandCheck(this, FormData) + + return makeIterator( + () => this[kState].map(pair => [pair.name, pair.value]), + 'FormData', + 'key+value' + ) + } + + keys () { + webidl.brandCheck(this, FormData) + + return makeIterator( + () => this[kState].map(pair => [pair.name, pair.value]), + 'FormData', + 'key' + ) + } + + values () { + webidl.brandCheck(this, FormData) + + return makeIterator( + () => this[kState].map(pair => [pair.name, pair.value]), + 'FormData', + 'value' + ) + } + + /** + * @param {(value: string, key: string, self: FormData) => void} callbackFn + * @param {unknown} thisArg + */ + forEach (callbackFn, thisArg = globalThis) { + webidl.brandCheck(this, FormData) + + webidl.argumentLengthCheck(arguments, 1, { header: 'FormData.forEach' }) + + if (typeof callbackFn !== 'function') { + throw new TypeError( + "Failed to execute 'forEach' on 'FormData': parameter 1 is not of type 'Function'." + ) + } + + for (const [key, value] of this) { + callbackFn.apply(thisArg, [value, key, this]) + } + } +} + +FormData.prototype[Symbol.iterator] = FormData.prototype.entries + +Object.defineProperties(FormData.prototype, { + [Symbol.toStringTag]: { + value: 'FormData', + configurable: true + } +}) + +/** + * @see https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#create-an-entry + * @param {string} name + * @param {string|Blob} value + * @param {?string} filename + * @returns + */ +function makeEntry (name, value, filename) { + // 1. Set name to the result of converting name into a scalar value string. + // "To convert a string into a scalar value string, replace any surrogates + // with U+FFFD." + // see: https://nodejs.org/dist/latest-v18.x/docs/api/buffer.html#buftostringencoding-start-end + name = Buffer.from(name).toString('utf8') + + // 2. If value is a string, then set value to the result of converting + // value into a scalar value string. + if (typeof value === 'string') { + value = Buffer.from(value).toString('utf8') + } else { + // 3. Otherwise: + + // 1. If value is not a File object, then set value to a new File object, + // representing the same bytes, whose name attribute value is "blob" + if (!isFileLike(value)) { + value = value instanceof Blob + ? new File([value], 'blob', { type: value.type }) + : new FileLike(value, 'blob', { type: value.type }) + } + + // 2. If filename is given, then set value to a new File object, + // representing the same bytes, whose name attribute is filename. + if (filename !== undefined) { + /** @type {FilePropertyBag} */ + const options = { + type: value.type, + lastModified: value.lastModified + } + + value = (NativeFile && value instanceof NativeFile) || value instanceof UndiciFile + ? new File([value], filename, options) + : new FileLike(value, filename, options) + } + } + + // 4. Return an entry whose name is name and whose value is value. + return { name, value } +} + +module.exports = { FormData } + + +/***/ }), + +/***/ 1246: +/***/ ((module) => { + +"use strict"; + + +// In case of breaking changes, increase the version +// number to avoid conflicts. +const globalOrigin = Symbol.for('undici.globalOrigin.1') + +function getGlobalOrigin () { + return globalThis[globalOrigin] +} + +function setGlobalOrigin (newOrigin) { + if (newOrigin === undefined) { + Object.defineProperty(globalThis, globalOrigin, { + value: undefined, + writable: true, + enumerable: false, + configurable: false + }) + + return + } + + const parsedURL = new URL(newOrigin) + + if (parsedURL.protocol !== 'http:' && parsedURL.protocol !== 'https:') { + throw new TypeError(`Only http & https urls are allowed, received ${parsedURL.protocol}`) + } + + Object.defineProperty(globalThis, globalOrigin, { + value: parsedURL, + writable: true, + enumerable: false, + configurable: false + }) +} + +module.exports = { + getGlobalOrigin, + setGlobalOrigin +} + + +/***/ }), + +/***/ 554: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; +// https://github.com/Ethan-Arrowood/undici-fetch + + + +const { kHeadersList } = __nccwpck_require__(2785) +const { kGuard } = __nccwpck_require__(5861) +const { kEnumerableProperty } = __nccwpck_require__(3983) +const { + makeIterator, + isValidHeaderName, + isValidHeaderValue +} = __nccwpck_require__(2538) +const { webidl } = __nccwpck_require__(1744) +const assert = __nccwpck_require__(9491) + +const kHeadersMap = Symbol('headers map') +const kHeadersSortedMap = Symbol('headers map sorted') + +/** + * @see https://fetch.spec.whatwg.org/#concept-header-value-normalize + * @param {string} potentialValue + */ +function headerValueNormalize (potentialValue) { + // To normalize a byte sequence potentialValue, remove + // any leading and trailing HTTP whitespace bytes from + // potentialValue. + + // Trimming the end with `.replace()` and a RegExp is typically subject to + // ReDoS. This is safer and faster. + let i = potentialValue.length + while (/[\r\n\t ]/.test(potentialValue.charAt(--i))); + return potentialValue.slice(0, i + 1).replace(/^[\r\n\t ]+/, '') +} + +function fill (headers, object) { + // To fill a Headers object headers with a given object object, run these steps: + + // 1. If object is a sequence, then for each header in object: + // Note: webidl conversion to array has already been done. + if (Array.isArray(object)) { + for (const header of object) { + // 1. If header does not contain exactly two items, then throw a TypeError. + if (header.length !== 2) { + throw webidl.errors.exception({ + header: 'Headers constructor', + message: `expected name/value pair to be length 2, found ${header.length}.` + }) + } + + // 2. Append (header’s first item, header’s second item) to headers. + headers.append(header[0], header[1]) + } + } else if (typeof object === 'object' && object !== null) { + // Note: null should throw + + // 2. Otherwise, object is a record, then for each key → value in object, + // append (key, value) to headers + for (const [key, value] of Object.entries(object)) { + headers.append(key, value) + } + } else { + throw webidl.errors.conversionFailed({ + prefix: 'Headers constructor', + argument: 'Argument 1', + types: ['sequence>', 'record'] + }) + } +} + +class HeadersList { + /** @type {[string, string][]|null} */ + cookies = null + + constructor (init) { + if (init instanceof HeadersList) { + this[kHeadersMap] = new Map(init[kHeadersMap]) + this[kHeadersSortedMap] = init[kHeadersSortedMap] + this.cookies = init.cookies + } else { + this[kHeadersMap] = new Map(init) + this[kHeadersSortedMap] = null + } + } + + // https://fetch.spec.whatwg.org/#header-list-contains + contains (name) { + // A header list list contains a header name name if list + // contains a header whose name is a byte-case-insensitive + // match for name. + name = name.toLowerCase() + + return this[kHeadersMap].has(name) + } + + clear () { + this[kHeadersMap].clear() + this[kHeadersSortedMap] = null + this.cookies = null + } + + // https://fetch.spec.whatwg.org/#concept-header-list-append + append (name, value) { + this[kHeadersSortedMap] = null + + // 1. If list contains name, then set name to the first such + // header’s name. + const lowercaseName = name.toLowerCase() + const exists = this[kHeadersMap].get(lowercaseName) + + // 2. Append (name, value) to list. + if (exists) { + const delimiter = lowercaseName === 'cookie' ? '; ' : ', ' + this[kHeadersMap].set(lowercaseName, { + name: exists.name, + value: `${exists.value}${delimiter}${value}` + }) + } else { + this[kHeadersMap].set(lowercaseName, { name, value }) + } + + if (lowercaseName === 'set-cookie') { + this.cookies ??= [] + this.cookies.push(value) + } + } + + // https://fetch.spec.whatwg.org/#concept-header-list-set + set (name, value) { + this[kHeadersSortedMap] = null + const lowercaseName = name.toLowerCase() + + if (lowercaseName === 'set-cookie') { + this.cookies = [value] + } + + // 1. If list contains name, then set the value of + // the first such header to value and remove the + // others. + // 2. Otherwise, append header (name, value) to list. + return this[kHeadersMap].set(lowercaseName, { name, value }) + } + + // https://fetch.spec.whatwg.org/#concept-header-list-delete + delete (name) { + this[kHeadersSortedMap] = null + + name = name.toLowerCase() + + if (name === 'set-cookie') { + this.cookies = null + } + + return this[kHeadersMap].delete(name) + } + + // https://fetch.spec.whatwg.org/#concept-header-list-get + get (name) { + // 1. If list does not contain name, then return null. + if (!this.contains(name)) { + return null + } + + // 2. Return the values of all headers in list whose name + // is a byte-case-insensitive match for name, + // separated from each other by 0x2C 0x20, in order. + return this[kHeadersMap].get(name.toLowerCase())?.value ?? null + } + + * [Symbol.iterator] () { + // use the lowercased name + for (const [name, { value }] of this[kHeadersMap]) { + yield [name, value] + } + } + + get entries () { + const headers = {} + + if (this[kHeadersMap].size) { + for (const { name, value } of this[kHeadersMap].values()) { + headers[name] = value + } + } + + return headers + } +} + +// https://fetch.spec.whatwg.org/#headers-class +class Headers { + constructor (init = undefined) { + this[kHeadersList] = new HeadersList() + + // The new Headers(init) constructor steps are: + + // 1. Set this’s guard to "none". + this[kGuard] = 'none' + + // 2. If init is given, then fill this with init. + if (init !== undefined) { + init = webidl.converters.HeadersInit(init) + fill(this, init) + } + } + + // https://fetch.spec.whatwg.org/#dom-headers-append + append (name, value) { + webidl.brandCheck(this, Headers) + + webidl.argumentLengthCheck(arguments, 2, { header: 'Headers.append' }) + + name = webidl.converters.ByteString(name) + value = webidl.converters.ByteString(value) + + // 1. Normalize value. + value = headerValueNormalize(value) + + // 2. If name is not a header name or value is not a + // header value, then throw a TypeError. + if (!isValidHeaderName(name)) { + throw webidl.errors.invalidArgument({ + prefix: 'Headers.append', + value: name, + type: 'header name' + }) + } else if (!isValidHeaderValue(value)) { + throw webidl.errors.invalidArgument({ + prefix: 'Headers.append', + value, + type: 'header value' + }) + } + + // 3. If headers’s guard is "immutable", then throw a TypeError. + // 4. Otherwise, if headers’s guard is "request" and name is a + // forbidden header name, return. + // Note: undici does not implement forbidden header names + if (this[kGuard] === 'immutable') { + throw new TypeError('immutable') + } else if (this[kGuard] === 'request-no-cors') { + // 5. Otherwise, if headers’s guard is "request-no-cors": + // TODO + } + + // 6. Otherwise, if headers’s guard is "response" and name is a + // forbidden response-header name, return. + + // 7. Append (name, value) to headers’s header list. + // 8. If headers’s guard is "request-no-cors", then remove + // privileged no-CORS request headers from headers + return this[kHeadersList].append(name, value) + } + + // https://fetch.spec.whatwg.org/#dom-headers-delete + delete (name) { + webidl.brandCheck(this, Headers) + + webidl.argumentLengthCheck(arguments, 1, { header: 'Headers.delete' }) + + name = webidl.converters.ByteString(name) + + // 1. If name is not a header name, then throw a TypeError. + if (!isValidHeaderName(name)) { + throw webidl.errors.invalidArgument({ + prefix: 'Headers.delete', + value: name, + type: 'header name' + }) + } + + // 2. If this’s guard is "immutable", then throw a TypeError. + // 3. Otherwise, if this’s guard is "request" and name is a + // forbidden header name, return. + // 4. Otherwise, if this’s guard is "request-no-cors", name + // is not a no-CORS-safelisted request-header name, and + // name is not a privileged no-CORS request-header name, + // return. + // 5. Otherwise, if this’s guard is "response" and name is + // a forbidden response-header name, return. + // Note: undici does not implement forbidden header names + if (this[kGuard] === 'immutable') { + throw new TypeError('immutable') + } else if (this[kGuard] === 'request-no-cors') { + // TODO + } + + // 6. If this’s header list does not contain name, then + // return. + if (!this[kHeadersList].contains(name)) { + return + } + + // 7. Delete name from this’s header list. + // 8. If this’s guard is "request-no-cors", then remove + // privileged no-CORS request headers from this. + return this[kHeadersList].delete(name) + } + + // https://fetch.spec.whatwg.org/#dom-headers-get + get (name) { + webidl.brandCheck(this, Headers) + + webidl.argumentLengthCheck(arguments, 1, { header: 'Headers.get' }) + + name = webidl.converters.ByteString(name) + + // 1. If name is not a header name, then throw a TypeError. + if (!isValidHeaderName(name)) { + throw webidl.errors.invalidArgument({ + prefix: 'Headers.get', + value: name, + type: 'header name' + }) + } + + // 2. Return the result of getting name from this’s header + // list. + return this[kHeadersList].get(name) + } + + // https://fetch.spec.whatwg.org/#dom-headers-has + has (name) { + webidl.brandCheck(this, Headers) + + webidl.argumentLengthCheck(arguments, 1, { header: 'Headers.has' }) + + name = webidl.converters.ByteString(name) + + // 1. If name is not a header name, then throw a TypeError. + if (!isValidHeaderName(name)) { + throw webidl.errors.invalidArgument({ + prefix: 'Headers.has', + value: name, + type: 'header name' + }) + } + + // 2. Return true if this’s header list contains name; + // otherwise false. + return this[kHeadersList].contains(name) + } + + // https://fetch.spec.whatwg.org/#dom-headers-set + set (name, value) { + webidl.brandCheck(this, Headers) + + webidl.argumentLengthCheck(arguments, 2, { header: 'Headers.set' }) + + name = webidl.converters.ByteString(name) + value = webidl.converters.ByteString(value) + + // 1. Normalize value. + value = headerValueNormalize(value) + + // 2. If name is not a header name or value is not a + // header value, then throw a TypeError. + if (!isValidHeaderName(name)) { + throw webidl.errors.invalidArgument({ + prefix: 'Headers.set', + value: name, + type: 'header name' + }) + } else if (!isValidHeaderValue(value)) { + throw webidl.errors.invalidArgument({ + prefix: 'Headers.set', + value, + type: 'header value' + }) + } + + // 3. If this’s guard is "immutable", then throw a TypeError. + // 4. Otherwise, if this’s guard is "request" and name is a + // forbidden header name, return. + // 5. Otherwise, if this’s guard is "request-no-cors" and + // name/value is not a no-CORS-safelisted request-header, + // return. + // 6. Otherwise, if this’s guard is "response" and name is a + // forbidden response-header name, return. + // Note: undici does not implement forbidden header names + if (this[kGuard] === 'immutable') { + throw new TypeError('immutable') + } else if (this[kGuard] === 'request-no-cors') { + // TODO + } + + // 7. Set (name, value) in this’s header list. + // 8. If this’s guard is "request-no-cors", then remove + // privileged no-CORS request headers from this + return this[kHeadersList].set(name, value) + } + + // https://fetch.spec.whatwg.org/#dom-headers-getsetcookie + getSetCookie () { + webidl.brandCheck(this, Headers) + + // 1. If this’s header list does not contain `Set-Cookie`, then return « ». + // 2. Return the values of all headers in this’s header list whose name is + // a byte-case-insensitive match for `Set-Cookie`, in order. + + const list = this[kHeadersList].cookies + + if (list) { + return [...list] + } + + return [] + } + + // https://fetch.spec.whatwg.org/#concept-header-list-sort-and-combine + get [kHeadersSortedMap] () { + if (this[kHeadersList][kHeadersSortedMap]) { + return this[kHeadersList][kHeadersSortedMap] + } + + // 1. Let headers be an empty list of headers with the key being the name + // and value the value. + const headers = [] + + // 2. Let names be the result of convert header names to a sorted-lowercase + // set with all the names of the headers in list. + const names = [...this[kHeadersList]].sort((a, b) => a[0] < b[0] ? -1 : 1) + const cookies = this[kHeadersList].cookies + + // 3. For each name of names: + for (const [name, value] of names) { + // 1. If name is `set-cookie`, then: + if (name === 'set-cookie') { + // 1. Let values be a list of all values of headers in list whose name + // is a byte-case-insensitive match for name, in order. + + // 2. For each value of values: + // 1. Append (name, value) to headers. + for (const value of cookies) { + headers.push([name, value]) + } + } else { + // 2. Otherwise: + + // 1. Let value be the result of getting name from list. + + // 2. Assert: value is non-null. + assert(value !== null) + + // 3. Append (name, value) to headers. + headers.push([name, value]) + } + } + + this[kHeadersList][kHeadersSortedMap] = headers + + // 4. Return headers. + return headers + } + + keys () { + webidl.brandCheck(this, Headers) + + return makeIterator( + () => [...this[kHeadersSortedMap].values()], + 'Headers', + 'key' + ) + } + + values () { + webidl.brandCheck(this, Headers) + + return makeIterator( + () => [...this[kHeadersSortedMap].values()], + 'Headers', + 'value' + ) + } + + entries () { + webidl.brandCheck(this, Headers) + + return makeIterator( + () => [...this[kHeadersSortedMap].values()], + 'Headers', + 'key+value' + ) + } + + /** + * @param {(value: string, key: string, self: Headers) => void} callbackFn + * @param {unknown} thisArg + */ + forEach (callbackFn, thisArg = globalThis) { + webidl.brandCheck(this, Headers) + + webidl.argumentLengthCheck(arguments, 1, { header: 'Headers.forEach' }) + + if (typeof callbackFn !== 'function') { + throw new TypeError( + "Failed to execute 'forEach' on 'Headers': parameter 1 is not of type 'Function'." + ) + } + + for (const [key, value] of this) { + callbackFn.apply(thisArg, [value, key, this]) + } + } + + [Symbol.for('nodejs.util.inspect.custom')] () { + webidl.brandCheck(this, Headers) + + return this[kHeadersList] + } +} + +Headers.prototype[Symbol.iterator] = Headers.prototype.entries + +Object.defineProperties(Headers.prototype, { + append: kEnumerableProperty, + delete: kEnumerableProperty, + get: kEnumerableProperty, + has: kEnumerableProperty, + set: kEnumerableProperty, + getSetCookie: kEnumerableProperty, + keys: kEnumerableProperty, + values: kEnumerableProperty, + entries: kEnumerableProperty, + forEach: kEnumerableProperty, + [Symbol.iterator]: { enumerable: false }, + [Symbol.toStringTag]: { + value: 'Headers', + configurable: true + } +}) + +webidl.converters.HeadersInit = function (V) { + if (webidl.util.Type(V) === 'Object') { + if (V[Symbol.iterator]) { + return webidl.converters['sequence>'](V) + } + + return webidl.converters['record'](V) + } + + throw webidl.errors.conversionFailed({ + prefix: 'Headers constructor', + argument: 'Argument 1', + types: ['sequence>', 'record'] + }) +} + +module.exports = { + fill, + Headers, + HeadersList +} + + +/***/ }), + +/***/ 4881: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; +// https://github.com/Ethan-Arrowood/undici-fetch + + + +const { + Response, + makeNetworkError, + makeAppropriateNetworkError, + filterResponse, + makeResponse +} = __nccwpck_require__(7823) +const { Headers } = __nccwpck_require__(554) +const { Request, makeRequest } = __nccwpck_require__(8359) +const zlib = __nccwpck_require__(9796) +const { + bytesMatch, + makePolicyContainer, + clonePolicyContainer, + requestBadPort, + TAOCheck, + appendRequestOriginHeader, + responseLocationURL, + requestCurrentURL, + setRequestReferrerPolicyOnRedirect, + tryUpgradeRequestToAPotentiallyTrustworthyURL, + createOpaqueTimingInfo, + appendFetchMetadata, + corsCheck, + crossOriginResourcePolicyCheck, + determineRequestsReferrer, + coarsenedSharedCurrentTime, + createDeferredPromise, + isBlobLike, + sameOrigin, + isCancelled, + isAborted, + isErrorLike, + fullyReadBody, + readableStreamClose, + isomorphicEncode, + urlIsLocal, + urlIsHttpHttpsScheme, + urlHasHttpsScheme +} = __nccwpck_require__(2538) +const { kState, kHeaders, kGuard, kRealm } = __nccwpck_require__(5861) +const assert = __nccwpck_require__(9491) +const { safelyExtractBody } = __nccwpck_require__(1472) +const { + redirectStatusSet, + nullBodyStatus, + safeMethodsSet, + requestBodyHeader, + subresourceSet, + DOMException +} = __nccwpck_require__(1037) +const { kHeadersList } = __nccwpck_require__(2785) +const EE = __nccwpck_require__(2361) +const { Readable, pipeline } = __nccwpck_require__(2781) +const { addAbortListener, isErrored, isReadable, nodeMajor, nodeMinor } = __nccwpck_require__(3983) +const { dataURLProcessor, serializeAMimeType } = __nccwpck_require__(685) +const { TransformStream } = __nccwpck_require__(5356) +const { getGlobalDispatcher } = __nccwpck_require__(1892) +const { webidl } = __nccwpck_require__(1744) +const { STATUS_CODES } = __nccwpck_require__(3685) +const GET_OR_HEAD = ['GET', 'HEAD'] + +/** @type {import('buffer').resolveObjectURL} */ +let resolveObjectURL +let ReadableStream = globalThis.ReadableStream + +class Fetch extends EE { + constructor (dispatcher) { + super() + + this.dispatcher = dispatcher + this.connection = null + this.dump = false + this.state = 'ongoing' + // 2 terminated listeners get added per request, + // but only 1 gets removed. If there are 20 redirects, + // 21 listeners will be added. + // See https://github.com/nodejs/undici/issues/1711 + // TODO (fix): Find and fix root cause for leaked listener. + this.setMaxListeners(21) + } + + terminate (reason) { + if (this.state !== 'ongoing') { + return + } + + this.state = 'terminated' + this.connection?.destroy(reason) + this.emit('terminated', reason) + } + + // https://fetch.spec.whatwg.org/#fetch-controller-abort + abort (error) { + if (this.state !== 'ongoing') { + return + } + + // 1. Set controller’s state to "aborted". + this.state = 'aborted' + + // 2. Let fallbackError be an "AbortError" DOMException. + // 3. Set error to fallbackError if it is not given. + if (!error) { + error = new DOMException('The operation was aborted.', 'AbortError') + } + + // 4. Let serializedError be StructuredSerialize(error). + // If that threw an exception, catch it, and let + // serializedError be StructuredSerialize(fallbackError). + + // 5. Set controller’s serialized abort reason to serializedError. + this.serializedAbortReason = error + + this.connection?.destroy(error) + this.emit('terminated', error) + } +} + +// https://fetch.spec.whatwg.org/#fetch-method +function fetch (input, init = {}) { + webidl.argumentLengthCheck(arguments, 1, { header: 'globalThis.fetch' }) + + // 1. Let p be a new promise. + const p = createDeferredPromise() + + // 2. Let requestObject be the result of invoking the initial value of + // Request as constructor with input and init as arguments. If this throws + // an exception, reject p with it and return p. + let requestObject + + try { + requestObject = new Request(input, init) + } catch (e) { + p.reject(e) + return p.promise + } + + // 3. Let request be requestObject’s request. + const request = requestObject[kState] + + // 4. If requestObject’s signal’s aborted flag is set, then: + if (requestObject.signal.aborted) { + // 1. Abort the fetch() call with p, request, null, and + // requestObject’s signal’s abort reason. + abortFetch(p, request, null, requestObject.signal.reason) + + // 2. Return p. + return p.promise + } + + // 5. Let globalObject be request’s client’s global object. + const globalObject = request.client.globalObject + + // 6. If globalObject is a ServiceWorkerGlobalScope object, then set + // request’s service-workers mode to "none". + if (globalObject?.constructor?.name === 'ServiceWorkerGlobalScope') { + request.serviceWorkers = 'none' + } + + // 7. Let responseObject be null. + let responseObject = null + + // 8. Let relevantRealm be this’s relevant Realm. + const relevantRealm = null + + // 9. Let locallyAborted be false. + let locallyAborted = false + + // 10. Let controller be null. + let controller = null + + // 11. Add the following abort steps to requestObject’s signal: + addAbortListener( + requestObject.signal, + () => { + // 1. Set locallyAborted to true. + locallyAborted = true + + // 2. Assert: controller is non-null. + assert(controller != null) + + // 3. Abort controller with requestObject’s signal’s abort reason. + controller.abort(requestObject.signal.reason) + + // 4. Abort the fetch() call with p, request, responseObject, + // and requestObject’s signal’s abort reason. + abortFetch(p, request, responseObject, requestObject.signal.reason) + } + ) + + // 12. Let handleFetchDone given response response be to finalize and + // report timing with response, globalObject, and "fetch". + const handleFetchDone = (response) => + finalizeAndReportTiming(response, 'fetch') + + // 13. Set controller to the result of calling fetch given request, + // with processResponseEndOfBody set to handleFetchDone, and processResponse + // given response being these substeps: + + const processResponse = (response) => { + // 1. If locallyAborted is true, terminate these substeps. + if (locallyAborted) { + return Promise.resolve() + } + + // 2. If response’s aborted flag is set, then: + if (response.aborted) { + // 1. Let deserializedError be the result of deserialize a serialized + // abort reason given controller’s serialized abort reason and + // relevantRealm. + + // 2. Abort the fetch() call with p, request, responseObject, and + // deserializedError. + + abortFetch(p, request, responseObject, controller.serializedAbortReason) + return Promise.resolve() + } + + // 3. If response is a network error, then reject p with a TypeError + // and terminate these substeps. + if (response.type === 'error') { + p.reject( + Object.assign(new TypeError('fetch failed'), { cause: response.error }) + ) + return Promise.resolve() + } + + // 4. Set responseObject to the result of creating a Response object, + // given response, "immutable", and relevantRealm. + responseObject = new Response() + responseObject[kState] = response + responseObject[kRealm] = relevantRealm + responseObject[kHeaders][kHeadersList] = response.headersList + responseObject[kHeaders][kGuard] = 'immutable' + responseObject[kHeaders][kRealm] = relevantRealm + + // 5. Resolve p with responseObject. + p.resolve(responseObject) + } + + controller = fetching({ + request, + processResponseEndOfBody: handleFetchDone, + processResponse, + dispatcher: init.dispatcher ?? getGlobalDispatcher() // undici + }) + + // 14. Return p. + return p.promise +} + +// https://fetch.spec.whatwg.org/#finalize-and-report-timing +function finalizeAndReportTiming (response, initiatorType = 'other') { + // 1. If response is an aborted network error, then return. + if (response.type === 'error' && response.aborted) { + return + } + + // 2. If response’s URL list is null or empty, then return. + if (!response.urlList?.length) { + return + } + + // 3. Let originalURL be response’s URL list[0]. + const originalURL = response.urlList[0] + + // 4. Let timingInfo be response’s timing info. + let timingInfo = response.timingInfo + + // 5. Let cacheState be response’s cache state. + let cacheState = response.cacheState + + // 6. If originalURL’s scheme is not an HTTP(S) scheme, then return. + if (!urlIsHttpHttpsScheme(originalURL)) { + return + } + + // 7. If timingInfo is null, then return. + if (timingInfo === null) { + return + } + + // 8. If response’s timing allow passed flag is not set, then: + if (!timingInfo.timingAllowPassed) { + // 1. Set timingInfo to a the result of creating an opaque timing info for timingInfo. + timingInfo = createOpaqueTimingInfo({ + startTime: timingInfo.startTime + }) + + // 2. Set cacheState to the empty string. + cacheState = '' + } + + // 9. Set timingInfo’s end time to the coarsened shared current time + // given global’s relevant settings object’s cross-origin isolated + // capability. + // TODO: given global’s relevant settings object’s cross-origin isolated + // capability? + timingInfo.endTime = coarsenedSharedCurrentTime() + + // 10. Set response’s timing info to timingInfo. + response.timingInfo = timingInfo + + // 11. Mark resource timing for timingInfo, originalURL, initiatorType, + // global, and cacheState. + markResourceTiming( + timingInfo, + originalURL, + initiatorType, + globalThis, + cacheState + ) +} + +// https://w3c.github.io/resource-timing/#dfn-mark-resource-timing +function markResourceTiming (timingInfo, originalURL, initiatorType, globalThis, cacheState) { + if (nodeMajor > 18 || (nodeMajor === 18 && nodeMinor >= 2)) { + performance.markResourceTiming(timingInfo, originalURL.href, initiatorType, globalThis, cacheState) + } +} + +// https://fetch.spec.whatwg.org/#abort-fetch +function abortFetch (p, request, responseObject, error) { + // Note: AbortSignal.reason was added in node v17.2.0 + // which would give us an undefined error to reject with. + // Remove this once node v16 is no longer supported. + if (!error) { + error = new DOMException('The operation was aborted.', 'AbortError') + } + + // 1. Reject promise with error. + p.reject(error) + + // 2. If request’s body is not null and is readable, then cancel request’s + // body with error. + if (request.body != null && isReadable(request.body?.stream)) { + request.body.stream.cancel(error).catch((err) => { + if (err.code === 'ERR_INVALID_STATE') { + // Node bug? + return + } + throw err + }) + } + + // 3. If responseObject is null, then return. + if (responseObject == null) { + return + } + + // 4. Let response be responseObject’s response. + const response = responseObject[kState] + + // 5. If response’s body is not null and is readable, then error response’s + // body with error. + if (response.body != null && isReadable(response.body?.stream)) { + response.body.stream.cancel(error).catch((err) => { + if (err.code === 'ERR_INVALID_STATE') { + // Node bug? + return + } + throw err + }) + } +} + +// https://fetch.spec.whatwg.org/#fetching +function fetching ({ + request, + processRequestBodyChunkLength, + processRequestEndOfBody, + processResponse, + processResponseEndOfBody, + processResponseConsumeBody, + useParallelQueue = false, + dispatcher // undici +}) { + // 1. Let taskDestination be null. + let taskDestination = null + + // 2. Let crossOriginIsolatedCapability be false. + let crossOriginIsolatedCapability = false + + // 3. If request’s client is non-null, then: + if (request.client != null) { + // 1. Set taskDestination to request’s client’s global object. + taskDestination = request.client.globalObject + + // 2. Set crossOriginIsolatedCapability to request’s client’s cross-origin + // isolated capability. + crossOriginIsolatedCapability = + request.client.crossOriginIsolatedCapability + } + + // 4. If useParallelQueue is true, then set taskDestination to the result of + // starting a new parallel queue. + // TODO + + // 5. Let timingInfo be a new fetch timing info whose start time and + // post-redirect start time are the coarsened shared current time given + // crossOriginIsolatedCapability. + const currenTime = coarsenedSharedCurrentTime(crossOriginIsolatedCapability) + const timingInfo = createOpaqueTimingInfo({ + startTime: currenTime + }) + + // 6. Let fetchParams be a new fetch params whose + // request is request, + // timing info is timingInfo, + // process request body chunk length is processRequestBodyChunkLength, + // process request end-of-body is processRequestEndOfBody, + // process response is processResponse, + // process response consume body is processResponseConsumeBody, + // process response end-of-body is processResponseEndOfBody, + // task destination is taskDestination, + // and cross-origin isolated capability is crossOriginIsolatedCapability. + const fetchParams = { + controller: new Fetch(dispatcher), + request, + timingInfo, + processRequestBodyChunkLength, + processRequestEndOfBody, + processResponse, + processResponseConsumeBody, + processResponseEndOfBody, + taskDestination, + crossOriginIsolatedCapability + } + + // 7. If request’s body is a byte sequence, then set request’s body to + // request’s body as a body. + // NOTE: Since fetching is only called from fetch, body should already be + // extracted. + assert(!request.body || request.body.stream) + + // 8. If request’s window is "client", then set request’s window to request’s + // client, if request’s client’s global object is a Window object; otherwise + // "no-window". + if (request.window === 'client') { + // TODO: What if request.client is null? + request.window = + request.client?.globalObject?.constructor?.name === 'Window' + ? request.client + : 'no-window' + } + + // 9. If request’s origin is "client", then set request’s origin to request’s + // client’s origin. + if (request.origin === 'client') { + // TODO: What if request.client is null? + request.origin = request.client?.origin + } + + // 10. If all of the following conditions are true: + // TODO + + // 11. If request’s policy container is "client", then: + if (request.policyContainer === 'client') { + // 1. If request’s client is non-null, then set request’s policy + // container to a clone of request’s client’s policy container. [HTML] + if (request.client != null) { + request.policyContainer = clonePolicyContainer( + request.client.policyContainer + ) + } else { + // 2. Otherwise, set request’s policy container to a new policy + // container. + request.policyContainer = makePolicyContainer() + } + } + + // 12. If request’s header list does not contain `Accept`, then: + if (!request.headersList.contains('accept')) { + // 1. Let value be `*/*`. + const value = '*/*' + + // 2. A user agent should set value to the first matching statement, if + // any, switching on request’s destination: + // "document" + // "frame" + // "iframe" + // `text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8` + // "image" + // `image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5` + // "style" + // `text/css,*/*;q=0.1` + // TODO + + // 3. Append `Accept`/value to request’s header list. + request.headersList.append('accept', value) + } + + // 13. If request’s header list does not contain `Accept-Language`, then + // user agents should append `Accept-Language`/an appropriate value to + // request’s header list. + if (!request.headersList.contains('accept-language')) { + request.headersList.append('accept-language', '*') + } + + // 14. If request’s priority is null, then use request’s initiator and + // destination appropriately in setting request’s priority to a + // user-agent-defined object. + if (request.priority === null) { + // TODO + } + + // 15. If request is a subresource request, then: + if (subresourceSet.has(request.destination)) { + // TODO + } + + // 16. Run main fetch given fetchParams. + mainFetch(fetchParams) + .catch(err => { + fetchParams.controller.terminate(err) + }) + + // 17. Return fetchParam's controller + return fetchParams.controller +} + +// https://fetch.spec.whatwg.org/#concept-main-fetch +async function mainFetch (fetchParams, recursive = false) { + // 1. Let request be fetchParams’s request. + const request = fetchParams.request + + // 2. Let response be null. + let response = null + + // 3. If request’s local-URLs-only flag is set and request’s current URL is + // not local, then set response to a network error. + if (request.localURLsOnly && !urlIsLocal(requestCurrentURL(request))) { + response = makeNetworkError('local URLs only') + } + + // 4. Run report Content Security Policy violations for request. + // TODO + + // 5. Upgrade request to a potentially trustworthy URL, if appropriate. + tryUpgradeRequestToAPotentiallyTrustworthyURL(request) + + // 6. If should request be blocked due to a bad port, should fetching request + // be blocked as mixed content, or should request be blocked by Content + // Security Policy returns blocked, then set response to a network error. + if (requestBadPort(request) === 'blocked') { + response = makeNetworkError('bad port') + } + // TODO: should fetching request be blocked as mixed content? + // TODO: should request be blocked by Content Security Policy? + + // 7. If request’s referrer policy is the empty string, then set request’s + // referrer policy to request’s policy container’s referrer policy. + if (request.referrerPolicy === '') { + request.referrerPolicy = request.policyContainer.referrerPolicy + } + + // 8. If request’s referrer is not "no-referrer", then set request’s + // referrer to the result of invoking determine request’s referrer. + if (request.referrer !== 'no-referrer') { + request.referrer = determineRequestsReferrer(request) + } + + // 9. Set request’s current URL’s scheme to "https" if all of the following + // conditions are true: + // - request’s current URL’s scheme is "http" + // - request’s current URL’s host is a domain + // - Matching request’s current URL’s host per Known HSTS Host Domain Name + // Matching results in either a superdomain match with an asserted + // includeSubDomains directive or a congruent match (with or without an + // asserted includeSubDomains directive). [HSTS] + // TODO + + // 10. If recursive is false, then run the remaining steps in parallel. + // TODO + + // 11. If response is null, then set response to the result of running + // the steps corresponding to the first matching statement: + if (response === null) { + response = await (async () => { + const currentURL = requestCurrentURL(request) + + if ( + // - request’s current URL’s origin is same origin with request’s origin, + // and request’s response tainting is "basic" + (sameOrigin(currentURL, request.url) && request.responseTainting === 'basic') || + // request’s current URL’s scheme is "data" + (currentURL.protocol === 'data:') || + // - request’s mode is "navigate" or "websocket" + (request.mode === 'navigate' || request.mode === 'websocket') + ) { + // 1. Set request’s response tainting to "basic". + request.responseTainting = 'basic' + + // 2. Return the result of running scheme fetch given fetchParams. + return await schemeFetch(fetchParams) + } + + // request’s mode is "same-origin" + if (request.mode === 'same-origin') { + // 1. Return a network error. + return makeNetworkError('request mode cannot be "same-origin"') + } + + // request’s mode is "no-cors" + if (request.mode === 'no-cors') { + // 1. If request’s redirect mode is not "follow", then return a network + // error. + if (request.redirect !== 'follow') { + return makeNetworkError( + 'redirect mode cannot be "follow" for "no-cors" request' + ) + } + + // 2. Set request’s response tainting to "opaque". + request.responseTainting = 'opaque' + + // 3. Return the result of running scheme fetch given fetchParams. + return await schemeFetch(fetchParams) + } + + // request’s current URL’s scheme is not an HTTP(S) scheme + if (!urlIsHttpHttpsScheme(requestCurrentURL(request))) { + // Return a network error. + return makeNetworkError('URL scheme must be a HTTP(S) scheme') + } + + // - request’s use-CORS-preflight flag is set + // - request’s unsafe-request flag is set and either request’s method is + // not a CORS-safelisted method or CORS-unsafe request-header names with + // request’s header list is not empty + // 1. Set request’s response tainting to "cors". + // 2. Let corsWithPreflightResponse be the result of running HTTP fetch + // given fetchParams and true. + // 3. If corsWithPreflightResponse is a network error, then clear cache + // entries using request. + // 4. Return corsWithPreflightResponse. + // TODO + + // Otherwise + // 1. Set request’s response tainting to "cors". + request.responseTainting = 'cors' + + // 2. Return the result of running HTTP fetch given fetchParams. + return await httpFetch(fetchParams) + })() + } + + // 12. If recursive is true, then return response. + if (recursive) { + return response + } + + // 13. If response is not a network error and response is not a filtered + // response, then: + if (response.status !== 0 && !response.internalResponse) { + // If request’s response tainting is "cors", then: + if (request.responseTainting === 'cors') { + // 1. Let headerNames be the result of extracting header list values + // given `Access-Control-Expose-Headers` and response’s header list. + // TODO + // 2. If request’s credentials mode is not "include" and headerNames + // contains `*`, then set response’s CORS-exposed header-name list to + // all unique header names in response’s header list. + // TODO + // 3. Otherwise, if headerNames is not null or failure, then set + // response’s CORS-exposed header-name list to headerNames. + // TODO + } + + // Set response to the following filtered response with response as its + // internal response, depending on request’s response tainting: + if (request.responseTainting === 'basic') { + response = filterResponse(response, 'basic') + } else if (request.responseTainting === 'cors') { + response = filterResponse(response, 'cors') + } else if (request.responseTainting === 'opaque') { + response = filterResponse(response, 'opaque') + } else { + assert(false) + } + } + + // 14. Let internalResponse be response, if response is a network error, + // and response’s internal response otherwise. + let internalResponse = + response.status === 0 ? response : response.internalResponse + + // 15. If internalResponse’s URL list is empty, then set it to a clone of + // request’s URL list. + if (internalResponse.urlList.length === 0) { + internalResponse.urlList.push(...request.urlList) + } + + // 16. If request’s timing allow failed flag is unset, then set + // internalResponse’s timing allow passed flag. + if (!request.timingAllowFailed) { + response.timingAllowPassed = true + } + + // 17. If response is not a network error and any of the following returns + // blocked + // - should internalResponse to request be blocked as mixed content + // - should internalResponse to request be blocked by Content Security Policy + // - should internalResponse to request be blocked due to its MIME type + // - should internalResponse to request be blocked due to nosniff + // TODO + + // 18. If response’s type is "opaque", internalResponse’s status is 206, + // internalResponse’s range-requested flag is set, and request’s header + // list does not contain `Range`, then set response and internalResponse + // to a network error. + if ( + response.type === 'opaque' && + internalResponse.status === 206 && + internalResponse.rangeRequested && + !request.headers.contains('range') + ) { + response = internalResponse = makeNetworkError() + } + + // 19. If response is not a network error and either request’s method is + // `HEAD` or `CONNECT`, or internalResponse’s status is a null body status, + // set internalResponse’s body to null and disregard any enqueuing toward + // it (if any). + if ( + response.status !== 0 && + (request.method === 'HEAD' || + request.method === 'CONNECT' || + nullBodyStatus.includes(internalResponse.status)) + ) { + internalResponse.body = null + fetchParams.controller.dump = true + } + + // 20. If request’s integrity metadata is not the empty string, then: + if (request.integrity) { + // 1. Let processBodyError be this step: run fetch finale given fetchParams + // and a network error. + const processBodyError = (reason) => + fetchFinale(fetchParams, makeNetworkError(reason)) + + // 2. If request’s response tainting is "opaque", or response’s body is null, + // then run processBodyError and abort these steps. + if (request.responseTainting === 'opaque' || response.body == null) { + processBodyError(response.error) + return + } + + // 3. Let processBody given bytes be these steps: + const processBody = (bytes) => { + // 1. If bytes do not match request’s integrity metadata, + // then run processBodyError and abort these steps. [SRI] + if (!bytesMatch(bytes, request.integrity)) { + processBodyError('integrity mismatch') + return + } + + // 2. Set response’s body to bytes as a body. + response.body = safelyExtractBody(bytes)[0] + + // 3. Run fetch finale given fetchParams and response. + fetchFinale(fetchParams, response) + } + + // 4. Fully read response’s body given processBody and processBodyError. + await fullyReadBody(response.body, processBody, processBodyError) + } else { + // 21. Otherwise, run fetch finale given fetchParams and response. + fetchFinale(fetchParams, response) + } +} + +// https://fetch.spec.whatwg.org/#concept-scheme-fetch +// given a fetch params fetchParams +function schemeFetch (fetchParams) { + // Note: since the connection is destroyed on redirect, which sets fetchParams to a + // cancelled state, we do not want this condition to trigger *unless* there have been + // no redirects. See https://github.com/nodejs/undici/issues/1776 + // 1. If fetchParams is canceled, then return the appropriate network error for fetchParams. + if (isCancelled(fetchParams) && fetchParams.request.redirectCount === 0) { + return Promise.resolve(makeAppropriateNetworkError(fetchParams)) + } + + // 2. Let request be fetchParams’s request. + const { request } = fetchParams + + const { protocol: scheme } = requestCurrentURL(request) + + // 3. Switch on request’s current URL’s scheme and run the associated steps: + switch (scheme) { + case 'about:': { + // If request’s current URL’s path is the string "blank", then return a new response + // whose status message is `OK`, header list is « (`Content-Type`, `text/html;charset=utf-8`) », + // and body is the empty byte sequence as a body. + + // Otherwise, return a network error. + return Promise.resolve(makeNetworkError('about scheme is not supported')) + } + case 'blob:': { + if (!resolveObjectURL) { + resolveObjectURL = (__nccwpck_require__(4300).resolveObjectURL) + } + + // 1. Let blobURLEntry be request’s current URL’s blob URL entry. + const blobURLEntry = requestCurrentURL(request) + + // https://github.com/web-platform-tests/wpt/blob/7b0ebaccc62b566a1965396e5be7bb2bc06f841f/FileAPI/url/resources/fetch-tests.js#L52-L56 + // Buffer.resolveObjectURL does not ignore URL queries. + if (blobURLEntry.search.length !== 0) { + return Promise.resolve(makeNetworkError('NetworkError when attempting to fetch resource.')) + } + + const blobURLEntryObject = resolveObjectURL(blobURLEntry.toString()) + + // 2. If request’s method is not `GET`, blobURLEntry is null, or blobURLEntry’s + // object is not a Blob object, then return a network error. + if (request.method !== 'GET' || !isBlobLike(blobURLEntryObject)) { + return Promise.resolve(makeNetworkError('invalid method')) + } + + // 3. Let bodyWithType be the result of safely extracting blobURLEntry’s object. + const bodyWithType = safelyExtractBody(blobURLEntryObject) + + // 4. Let body be bodyWithType’s body. + const body = bodyWithType[0] + + // 5. Let length be body’s length, serialized and isomorphic encoded. + const length = isomorphicEncode(`${body.length}`) + + // 6. Let type be bodyWithType’s type if it is non-null; otherwise the empty byte sequence. + const type = bodyWithType[1] ?? '' + + // 7. Return a new response whose status message is `OK`, header list is + // « (`Content-Length`, length), (`Content-Type`, type) », and body is body. + const response = makeResponse({ + statusText: 'OK', + headersList: [ + ['content-length', { name: 'Content-Length', value: length }], + ['content-type', { name: 'Content-Type', value: type }] + ] + }) + + response.body = body + + return Promise.resolve(response) + } + case 'data:': { + // 1. Let dataURLStruct be the result of running the + // data: URL processor on request’s current URL. + const currentURL = requestCurrentURL(request) + const dataURLStruct = dataURLProcessor(currentURL) + + // 2. If dataURLStruct is failure, then return a + // network error. + if (dataURLStruct === 'failure') { + return Promise.resolve(makeNetworkError('failed to fetch the data URL')) + } + + // 3. Let mimeType be dataURLStruct’s MIME type, serialized. + const mimeType = serializeAMimeType(dataURLStruct.mimeType) + + // 4. Return a response whose status message is `OK`, + // header list is « (`Content-Type`, mimeType) », + // and body is dataURLStruct’s body as a body. + return Promise.resolve(makeResponse({ + statusText: 'OK', + headersList: [ + ['content-type', { name: 'Content-Type', value: mimeType }] + ], + body: safelyExtractBody(dataURLStruct.body)[0] + })) + } + case 'file:': { + // For now, unfortunate as it is, file URLs are left as an exercise for the reader. + // When in doubt, return a network error. + return Promise.resolve(makeNetworkError('not implemented... yet...')) + } + case 'http:': + case 'https:': { + // Return the result of running HTTP fetch given fetchParams. + + return httpFetch(fetchParams) + .catch((err) => makeNetworkError(err)) + } + default: { + return Promise.resolve(makeNetworkError('unknown scheme')) + } + } +} + +// https://fetch.spec.whatwg.org/#finalize-response +function finalizeResponse (fetchParams, response) { + // 1. Set fetchParams’s request’s done flag. + fetchParams.request.done = true + + // 2, If fetchParams’s process response done is not null, then queue a fetch + // task to run fetchParams’s process response done given response, with + // fetchParams’s task destination. + if (fetchParams.processResponseDone != null) { + queueMicrotask(() => fetchParams.processResponseDone(response)) + } +} + +// https://fetch.spec.whatwg.org/#fetch-finale +function fetchFinale (fetchParams, response) { + // 1. If response is a network error, then: + if (response.type === 'error') { + // 1. Set response’s URL list to « fetchParams’s request’s URL list[0] ». + response.urlList = [fetchParams.request.urlList[0]] + + // 2. Set response’s timing info to the result of creating an opaque timing + // info for fetchParams’s timing info. + response.timingInfo = createOpaqueTimingInfo({ + startTime: fetchParams.timingInfo.startTime + }) + } + + // 2. Let processResponseEndOfBody be the following steps: + const processResponseEndOfBody = () => { + // 1. Set fetchParams’s request’s done flag. + fetchParams.request.done = true + + // If fetchParams’s process response end-of-body is not null, + // then queue a fetch task to run fetchParams’s process response + // end-of-body given response with fetchParams’s task destination. + if (fetchParams.processResponseEndOfBody != null) { + queueMicrotask(() => fetchParams.processResponseEndOfBody(response)) + } + } + + // 3. If fetchParams’s process response is non-null, then queue a fetch task + // to run fetchParams’s process response given response, with fetchParams’s + // task destination. + if (fetchParams.processResponse != null) { + queueMicrotask(() => fetchParams.processResponse(response)) + } + + // 4. If response’s body is null, then run processResponseEndOfBody. + if (response.body == null) { + processResponseEndOfBody() + } else { + // 5. Otherwise: + + // 1. Let transformStream be a new a TransformStream. + + // 2. Let identityTransformAlgorithm be an algorithm which, given chunk, + // enqueues chunk in transformStream. + const identityTransformAlgorithm = (chunk, controller) => { + controller.enqueue(chunk) + } + + // 3. Set up transformStream with transformAlgorithm set to identityTransformAlgorithm + // and flushAlgorithm set to processResponseEndOfBody. + const transformStream = new TransformStream({ + start () {}, + transform: identityTransformAlgorithm, + flush: processResponseEndOfBody + }, { + size () { + return 1 + } + }, { + size () { + return 1 + } + }) + + // 4. Set response’s body to the result of piping response’s body through transformStream. + response.body = { stream: response.body.stream.pipeThrough(transformStream) } + } + + // 6. If fetchParams’s process response consume body is non-null, then: + if (fetchParams.processResponseConsumeBody != null) { + // 1. Let processBody given nullOrBytes be this step: run fetchParams’s + // process response consume body given response and nullOrBytes. + const processBody = (nullOrBytes) => fetchParams.processResponseConsumeBody(response, nullOrBytes) + + // 2. Let processBodyError be this step: run fetchParams’s process + // response consume body given response and failure. + const processBodyError = (failure) => fetchParams.processResponseConsumeBody(response, failure) + + // 3. If response’s body is null, then queue a fetch task to run processBody + // given null, with fetchParams’s task destination. + if (response.body == null) { + queueMicrotask(() => processBody(null)) + } else { + // 4. Otherwise, fully read response’s body given processBody, processBodyError, + // and fetchParams’s task destination. + return fullyReadBody(response.body, processBody, processBodyError) + } + return Promise.resolve() + } +} + +// https://fetch.spec.whatwg.org/#http-fetch +async function httpFetch (fetchParams) { + // 1. Let request be fetchParams’s request. + const request = fetchParams.request + + // 2. Let response be null. + let response = null + + // 3. Let actualResponse be null. + let actualResponse = null + + // 4. Let timingInfo be fetchParams’s timing info. + const timingInfo = fetchParams.timingInfo + + // 5. If request’s service-workers mode is "all", then: + if (request.serviceWorkers === 'all') { + // TODO + } + + // 6. If response is null, then: + if (response === null) { + // 1. If makeCORSPreflight is true and one of these conditions is true: + // TODO + + // 2. If request’s redirect mode is "follow", then set request’s + // service-workers mode to "none". + if (request.redirect === 'follow') { + request.serviceWorkers = 'none' + } + + // 3. Set response and actualResponse to the result of running + // HTTP-network-or-cache fetch given fetchParams. + actualResponse = response = await httpNetworkOrCacheFetch(fetchParams) + + // 4. If request’s response tainting is "cors" and a CORS check + // for request and response returns failure, then return a network error. + if ( + request.responseTainting === 'cors' && + corsCheck(request, response) === 'failure' + ) { + return makeNetworkError('cors failure') + } + + // 5. If the TAO check for request and response returns failure, then set + // request’s timing allow failed flag. + if (TAOCheck(request, response) === 'failure') { + request.timingAllowFailed = true + } + } + + // 7. If either request’s response tainting or response’s type + // is "opaque", and the cross-origin resource policy check with + // request’s origin, request’s client, request’s destination, + // and actualResponse returns blocked, then return a network error. + if ( + (request.responseTainting === 'opaque' || response.type === 'opaque') && + crossOriginResourcePolicyCheck( + request.origin, + request.client, + request.destination, + actualResponse + ) === 'blocked' + ) { + return makeNetworkError('blocked') + } + + // 8. If actualResponse’s status is a redirect status, then: + if (redirectStatusSet.has(actualResponse.status)) { + // 1. If actualResponse’s status is not 303, request’s body is not null, + // and the connection uses HTTP/2, then user agents may, and are even + // encouraged to, transmit an RST_STREAM frame. + // See, https://github.com/whatwg/fetch/issues/1288 + if (request.redirect !== 'manual') { + fetchParams.controller.connection.destroy() + } + + // 2. Switch on request’s redirect mode: + if (request.redirect === 'error') { + // Set response to a network error. + response = makeNetworkError('unexpected redirect') + } else if (request.redirect === 'manual') { + // Set response to an opaque-redirect filtered response whose internal + // response is actualResponse. + // NOTE(spec): On the web this would return an `opaqueredirect` response, + // but that doesn't make sense server side. + // See https://github.com/nodejs/undici/issues/1193. + response = actualResponse + } else if (request.redirect === 'follow') { + // Set response to the result of running HTTP-redirect fetch given + // fetchParams and response. + response = await httpRedirectFetch(fetchParams, response) + } else { + assert(false) + } + } + + // 9. Set response’s timing info to timingInfo. + response.timingInfo = timingInfo + + // 10. Return response. + return response +} + +// https://fetch.spec.whatwg.org/#http-redirect-fetch +function httpRedirectFetch (fetchParams, response) { + // 1. Let request be fetchParams’s request. + const request = fetchParams.request + + // 2. Let actualResponse be response, if response is not a filtered response, + // and response’s internal response otherwise. + const actualResponse = response.internalResponse + ? response.internalResponse + : response + + // 3. Let locationURL be actualResponse’s location URL given request’s current + // URL’s fragment. + let locationURL + + try { + locationURL = responseLocationURL( + actualResponse, + requestCurrentURL(request).hash + ) + + // 4. If locationURL is null, then return response. + if (locationURL == null) { + return response + } + } catch (err) { + // 5. If locationURL is failure, then return a network error. + return Promise.resolve(makeNetworkError(err)) + } + + // 6. If locationURL’s scheme is not an HTTP(S) scheme, then return a network + // error. + if (!urlIsHttpHttpsScheme(locationURL)) { + return Promise.resolve(makeNetworkError('URL scheme must be a HTTP(S) scheme')) + } + + // 7. If request’s redirect count is 20, then return a network error. + if (request.redirectCount === 20) { + return Promise.resolve(makeNetworkError('redirect count exceeded')) + } + + // 8. Increase request’s redirect count by 1. + request.redirectCount += 1 + + // 9. If request’s mode is "cors", locationURL includes credentials, and + // request’s origin is not same origin with locationURL’s origin, then return + // a network error. + if ( + request.mode === 'cors' && + (locationURL.username || locationURL.password) && + !sameOrigin(request, locationURL) + ) { + return Promise.resolve(makeNetworkError('cross origin not allowed for request mode "cors"')) + } + + // 10. If request’s response tainting is "cors" and locationURL includes + // credentials, then return a network error. + if ( + request.responseTainting === 'cors' && + (locationURL.username || locationURL.password) + ) { + return Promise.resolve(makeNetworkError( + 'URL cannot contain credentials for request mode "cors"' + )) + } + + // 11. If actualResponse’s status is not 303, request’s body is non-null, + // and request’s body’s source is null, then return a network error. + if ( + actualResponse.status !== 303 && + request.body != null && + request.body.source == null + ) { + return Promise.resolve(makeNetworkError()) + } + + // 12. If one of the following is true + // - actualResponse’s status is 301 or 302 and request’s method is `POST` + // - actualResponse’s status is 303 and request’s method is not `GET` or `HEAD` + if ( + ([301, 302].includes(actualResponse.status) && request.method === 'POST') || + (actualResponse.status === 303 && + !GET_OR_HEAD.includes(request.method)) + ) { + // then: + // 1. Set request’s method to `GET` and request’s body to null. + request.method = 'GET' + request.body = null + + // 2. For each headerName of request-body-header name, delete headerName from + // request’s header list. + for (const headerName of requestBodyHeader) { + request.headersList.delete(headerName) + } + } + + // 13. If request’s current URL’s origin is not same origin with locationURL’s + // origin, then for each headerName of CORS non-wildcard request-header name, + // delete headerName from request’s header list. + if (!sameOrigin(requestCurrentURL(request), locationURL)) { + // https://fetch.spec.whatwg.org/#cors-non-wildcard-request-header-name + request.headersList.delete('authorization') + + // "Cookie" and "Host" are forbidden request-headers, which undici doesn't implement. + request.headersList.delete('cookie') + request.headersList.delete('host') + } + + // 14. If request’s body is non-null, then set request’s body to the first return + // value of safely extracting request’s body’s source. + if (request.body != null) { + assert(request.body.source != null) + request.body = safelyExtractBody(request.body.source)[0] + } + + // 15. Let timingInfo be fetchParams’s timing info. + const timingInfo = fetchParams.timingInfo + + // 16. Set timingInfo’s redirect end time and post-redirect start time to the + // coarsened shared current time given fetchParams’s cross-origin isolated + // capability. + timingInfo.redirectEndTime = timingInfo.postRedirectStartTime = + coarsenedSharedCurrentTime(fetchParams.crossOriginIsolatedCapability) + + // 17. If timingInfo’s redirect start time is 0, then set timingInfo’s + // redirect start time to timingInfo’s start time. + if (timingInfo.redirectStartTime === 0) { + timingInfo.redirectStartTime = timingInfo.startTime + } + + // 18. Append locationURL to request’s URL list. + request.urlList.push(locationURL) + + // 19. Invoke set request’s referrer policy on redirect on request and + // actualResponse. + setRequestReferrerPolicyOnRedirect(request, actualResponse) + + // 20. Return the result of running main fetch given fetchParams and true. + return mainFetch(fetchParams, true) +} + +// https://fetch.spec.whatwg.org/#http-network-or-cache-fetch +async function httpNetworkOrCacheFetch ( + fetchParams, + isAuthenticationFetch = false, + isNewConnectionFetch = false +) { + // 1. Let request be fetchParams’s request. + const request = fetchParams.request + + // 2. Let httpFetchParams be null. + let httpFetchParams = null + + // 3. Let httpRequest be null. + let httpRequest = null + + // 4. Let response be null. + let response = null + + // 5. Let storedResponse be null. + // TODO: cache + + // 6. Let httpCache be null. + const httpCache = null + + // 7. Let the revalidatingFlag be unset. + const revalidatingFlag = false + + // 8. Run these steps, but abort when the ongoing fetch is terminated: + + // 1. If request’s window is "no-window" and request’s redirect mode is + // "error", then set httpFetchParams to fetchParams and httpRequest to + // request. + if (request.window === 'no-window' && request.redirect === 'error') { + httpFetchParams = fetchParams + httpRequest = request + } else { + // Otherwise: + + // 1. Set httpRequest to a clone of request. + httpRequest = makeRequest(request) + + // 2. Set httpFetchParams to a copy of fetchParams. + httpFetchParams = { ...fetchParams } + + // 3. Set httpFetchParams’s request to httpRequest. + httpFetchParams.request = httpRequest + } + + // 3. Let includeCredentials be true if one of + const includeCredentials = + request.credentials === 'include' || + (request.credentials === 'same-origin' && + request.responseTainting === 'basic') + + // 4. Let contentLength be httpRequest’s body’s length, if httpRequest’s + // body is non-null; otherwise null. + const contentLength = httpRequest.body ? httpRequest.body.length : null + + // 5. Let contentLengthHeaderValue be null. + let contentLengthHeaderValue = null + + // 6. If httpRequest’s body is null and httpRequest’s method is `POST` or + // `PUT`, then set contentLengthHeaderValue to `0`. + if ( + httpRequest.body == null && + ['POST', 'PUT'].includes(httpRequest.method) + ) { + contentLengthHeaderValue = '0' + } + + // 7. If contentLength is non-null, then set contentLengthHeaderValue to + // contentLength, serialized and isomorphic encoded. + if (contentLength != null) { + contentLengthHeaderValue = isomorphicEncode(`${contentLength}`) + } + + // 8. If contentLengthHeaderValue is non-null, then append + // `Content-Length`/contentLengthHeaderValue to httpRequest’s header + // list. + if (contentLengthHeaderValue != null) { + httpRequest.headersList.append('content-length', contentLengthHeaderValue) + } + + // 9. If contentLengthHeaderValue is non-null, then append (`Content-Length`, + // contentLengthHeaderValue) to httpRequest’s header list. + + // 10. If contentLength is non-null and httpRequest’s keepalive is true, + // then: + if (contentLength != null && httpRequest.keepalive) { + // NOTE: keepalive is a noop outside of browser context. + } + + // 11. If httpRequest’s referrer is a URL, then append + // `Referer`/httpRequest’s referrer, serialized and isomorphic encoded, + // to httpRequest’s header list. + if (httpRequest.referrer instanceof URL) { + httpRequest.headersList.append('referer', isomorphicEncode(httpRequest.referrer.href)) + } + + // 12. Append a request `Origin` header for httpRequest. + appendRequestOriginHeader(httpRequest) + + // 13. Append the Fetch metadata headers for httpRequest. [FETCH-METADATA] + appendFetchMetadata(httpRequest) + + // 14. If httpRequest’s header list does not contain `User-Agent`, then + // user agents should append `User-Agent`/default `User-Agent` value to + // httpRequest’s header list. + if (!httpRequest.headersList.contains('user-agent')) { + httpRequest.headersList.append('user-agent', typeof esbuildDetection === 'undefined' ? 'undici' : 'node') + } + + // 15. If httpRequest’s cache mode is "default" and httpRequest’s header + // list contains `If-Modified-Since`, `If-None-Match`, + // `If-Unmodified-Since`, `If-Match`, or `If-Range`, then set + // httpRequest’s cache mode to "no-store". + if ( + httpRequest.cache === 'default' && + (httpRequest.headersList.contains('if-modified-since') || + httpRequest.headersList.contains('if-none-match') || + httpRequest.headersList.contains('if-unmodified-since') || + httpRequest.headersList.contains('if-match') || + httpRequest.headersList.contains('if-range')) + ) { + httpRequest.cache = 'no-store' + } + + // 16. If httpRequest’s cache mode is "no-cache", httpRequest’s prevent + // no-cache cache-control header modification flag is unset, and + // httpRequest’s header list does not contain `Cache-Control`, then append + // `Cache-Control`/`max-age=0` to httpRequest’s header list. + if ( + httpRequest.cache === 'no-cache' && + !httpRequest.preventNoCacheCacheControlHeaderModification && + !httpRequest.headersList.contains('cache-control') + ) { + httpRequest.headersList.append('cache-control', 'max-age=0') + } + + // 17. If httpRequest’s cache mode is "no-store" or "reload", then: + if (httpRequest.cache === 'no-store' || httpRequest.cache === 'reload') { + // 1. If httpRequest’s header list does not contain `Pragma`, then append + // `Pragma`/`no-cache` to httpRequest’s header list. + if (!httpRequest.headersList.contains('pragma')) { + httpRequest.headersList.append('pragma', 'no-cache') + } + + // 2. If httpRequest’s header list does not contain `Cache-Control`, + // then append `Cache-Control`/`no-cache` to httpRequest’s header list. + if (!httpRequest.headersList.contains('cache-control')) { + httpRequest.headersList.append('cache-control', 'no-cache') + } + } + + // 18. If httpRequest’s header list contains `Range`, then append + // `Accept-Encoding`/`identity` to httpRequest’s header list. + if (httpRequest.headersList.contains('range')) { + httpRequest.headersList.append('accept-encoding', 'identity') + } + + // 19. Modify httpRequest’s header list per HTTP. Do not append a given + // header if httpRequest’s header list contains that header’s name. + // TODO: https://github.com/whatwg/fetch/issues/1285#issuecomment-896560129 + if (!httpRequest.headersList.contains('accept-encoding')) { + if (urlHasHttpsScheme(requestCurrentURL(httpRequest))) { + httpRequest.headersList.append('accept-encoding', 'br, gzip, deflate') + } else { + httpRequest.headersList.append('accept-encoding', 'gzip, deflate') + } + } + + httpRequest.headersList.delete('host') + + // 20. If includeCredentials is true, then: + if (includeCredentials) { + // 1. If the user agent is not configured to block cookies for httpRequest + // (see section 7 of [COOKIES]), then: + // TODO: credentials + // 2. If httpRequest’s header list does not contain `Authorization`, then: + // TODO: credentials + } + + // 21. If there’s a proxy-authentication entry, use it as appropriate. + // TODO: proxy-authentication + + // 22. Set httpCache to the result of determining the HTTP cache + // partition, given httpRequest. + // TODO: cache + + // 23. If httpCache is null, then set httpRequest’s cache mode to + // "no-store". + if (httpCache == null) { + httpRequest.cache = 'no-store' + } + + // 24. If httpRequest’s cache mode is neither "no-store" nor "reload", + // then: + if (httpRequest.mode !== 'no-store' && httpRequest.mode !== 'reload') { + // TODO: cache + } + + // 9. If aborted, then return the appropriate network error for fetchParams. + // TODO + + // 10. If response is null, then: + if (response == null) { + // 1. If httpRequest’s cache mode is "only-if-cached", then return a + // network error. + if (httpRequest.mode === 'only-if-cached') { + return makeNetworkError('only if cached') + } + + // 2. Let forwardResponse be the result of running HTTP-network fetch + // given httpFetchParams, includeCredentials, and isNewConnectionFetch. + const forwardResponse = await httpNetworkFetch( + httpFetchParams, + includeCredentials, + isNewConnectionFetch + ) + + // 3. If httpRequest’s method is unsafe and forwardResponse’s status is + // in the range 200 to 399, inclusive, invalidate appropriate stored + // responses in httpCache, as per the "Invalidation" chapter of HTTP + // Caching, and set storedResponse to null. [HTTP-CACHING] + if ( + !safeMethodsSet.has(httpRequest.method) && + forwardResponse.status >= 200 && + forwardResponse.status <= 399 + ) { + // TODO: cache + } + + // 4. If the revalidatingFlag is set and forwardResponse’s status is 304, + // then: + if (revalidatingFlag && forwardResponse.status === 304) { + // TODO: cache + } + + // 5. If response is null, then: + if (response == null) { + // 1. Set response to forwardResponse. + response = forwardResponse + + // 2. Store httpRequest and forwardResponse in httpCache, as per the + // "Storing Responses in Caches" chapter of HTTP Caching. [HTTP-CACHING] + // TODO: cache + } + } + + // 11. Set response’s URL list to a clone of httpRequest’s URL list. + response.urlList = [...httpRequest.urlList] + + // 12. If httpRequest’s header list contains `Range`, then set response’s + // range-requested flag. + if (httpRequest.headersList.contains('range')) { + response.rangeRequested = true + } + + // 13. Set response’s request-includes-credentials to includeCredentials. + response.requestIncludesCredentials = includeCredentials + + // 14. If response’s status is 401, httpRequest’s response tainting is not + // "cors", includeCredentials is true, and request’s window is an environment + // settings object, then: + // TODO + + // 15. If response’s status is 407, then: + if (response.status === 407) { + // 1. If request’s window is "no-window", then return a network error. + if (request.window === 'no-window') { + return makeNetworkError() + } + + // 2. ??? + + // 3. If fetchParams is canceled, then return the appropriate network error for fetchParams. + if (isCancelled(fetchParams)) { + return makeAppropriateNetworkError(fetchParams) + } + + // 4. Prompt the end user as appropriate in request’s window and store + // the result as a proxy-authentication entry. [HTTP-AUTH] + // TODO: Invoke some kind of callback? + + // 5. Set response to the result of running HTTP-network-or-cache fetch given + // fetchParams. + // TODO + return makeNetworkError('proxy authentication required') + } + + // 16. If all of the following are true + if ( + // response’s status is 421 + response.status === 421 && + // isNewConnectionFetch is false + !isNewConnectionFetch && + // request’s body is null, or request’s body is non-null and request’s body’s source is non-null + (request.body == null || request.body.source != null) + ) { + // then: + + // 1. If fetchParams is canceled, then return the appropriate network error for fetchParams. + if (isCancelled(fetchParams)) { + return makeAppropriateNetworkError(fetchParams) + } + + // 2. Set response to the result of running HTTP-network-or-cache + // fetch given fetchParams, isAuthenticationFetch, and true. + + // TODO (spec): The spec doesn't specify this but we need to cancel + // the active response before we can start a new one. + // https://github.com/whatwg/fetch/issues/1293 + fetchParams.controller.connection.destroy() + + response = await httpNetworkOrCacheFetch( + fetchParams, + isAuthenticationFetch, + true + ) + } + + // 17. If isAuthenticationFetch is true, then create an authentication entry + if (isAuthenticationFetch) { + // TODO + } + + // 18. Return response. + return response +} + +// https://fetch.spec.whatwg.org/#http-network-fetch +async function httpNetworkFetch ( + fetchParams, + includeCredentials = false, + forceNewConnection = false +) { + assert(!fetchParams.controller.connection || fetchParams.controller.connection.destroyed) + + fetchParams.controller.connection = { + abort: null, + destroyed: false, + destroy (err) { + if (!this.destroyed) { + this.destroyed = true + this.abort?.(err ?? new DOMException('The operation was aborted.', 'AbortError')) + } + } + } + + // 1. Let request be fetchParams’s request. + const request = fetchParams.request + + // 2. Let response be null. + let response = null + + // 3. Let timingInfo be fetchParams’s timing info. + const timingInfo = fetchParams.timingInfo + + // 4. Let httpCache be the result of determining the HTTP cache partition, + // given request. + // TODO: cache + const httpCache = null + + // 5. If httpCache is null, then set request’s cache mode to "no-store". + if (httpCache == null) { + request.cache = 'no-store' + } + + // 6. Let networkPartitionKey be the result of determining the network + // partition key given request. + // TODO + + // 7. Let newConnection be "yes" if forceNewConnection is true; otherwise + // "no". + const newConnection = forceNewConnection ? 'yes' : 'no' // eslint-disable-line no-unused-vars + + // 8. Switch on request’s mode: + if (request.mode === 'websocket') { + // Let connection be the result of obtaining a WebSocket connection, + // given request’s current URL. + // TODO + } else { + // Let connection be the result of obtaining a connection, given + // networkPartitionKey, request’s current URL’s origin, + // includeCredentials, and forceNewConnection. + // TODO + } + + // 9. Run these steps, but abort when the ongoing fetch is terminated: + + // 1. If connection is failure, then return a network error. + + // 2. Set timingInfo’s final connection timing info to the result of + // calling clamp and coarsen connection timing info with connection’s + // timing info, timingInfo’s post-redirect start time, and fetchParams’s + // cross-origin isolated capability. + + // 3. If connection is not an HTTP/2 connection, request’s body is non-null, + // and request’s body’s source is null, then append (`Transfer-Encoding`, + // `chunked`) to request’s header list. + + // 4. Set timingInfo’s final network-request start time to the coarsened + // shared current time given fetchParams’s cross-origin isolated + // capability. + + // 5. Set response to the result of making an HTTP request over connection + // using request with the following caveats: + + // - Follow the relevant requirements from HTTP. [HTTP] [HTTP-SEMANTICS] + // [HTTP-COND] [HTTP-CACHING] [HTTP-AUTH] + + // - If request’s body is non-null, and request’s body’s source is null, + // then the user agent may have a buffer of up to 64 kibibytes and store + // a part of request’s body in that buffer. If the user agent reads from + // request’s body beyond that buffer’s size and the user agent needs to + // resend request, then instead return a network error. + + // - Set timingInfo’s final network-response start time to the coarsened + // shared current time given fetchParams’s cross-origin isolated capability, + // immediately after the user agent’s HTTP parser receives the first byte + // of the response (e.g., frame header bytes for HTTP/2 or response status + // line for HTTP/1.x). + + // - Wait until all the headers are transmitted. + + // - Any responses whose status is in the range 100 to 199, inclusive, + // and is not 101, are to be ignored, except for the purposes of setting + // timingInfo’s final network-response start time above. + + // - If request’s header list contains `Transfer-Encoding`/`chunked` and + // response is transferred via HTTP/1.0 or older, then return a network + // error. + + // - If the HTTP request results in a TLS client certificate dialog, then: + + // 1. If request’s window is an environment settings object, make the + // dialog available in request’s window. + + // 2. Otherwise, return a network error. + + // To transmit request’s body body, run these steps: + let requestBody = null + // 1. If body is null and fetchParams’s process request end-of-body is + // non-null, then queue a fetch task given fetchParams’s process request + // end-of-body and fetchParams’s task destination. + if (request.body == null && fetchParams.processRequestEndOfBody) { + queueMicrotask(() => fetchParams.processRequestEndOfBody()) + } else if (request.body != null) { + // 2. Otherwise, if body is non-null: + + // 1. Let processBodyChunk given bytes be these steps: + const processBodyChunk = async function * (bytes) { + // 1. If the ongoing fetch is terminated, then abort these steps. + if (isCancelled(fetchParams)) { + return + } + + // 2. Run this step in parallel: transmit bytes. + yield bytes + + // 3. If fetchParams’s process request body is non-null, then run + // fetchParams’s process request body given bytes’s length. + fetchParams.processRequestBodyChunkLength?.(bytes.byteLength) + } + + // 2. Let processEndOfBody be these steps: + const processEndOfBody = () => { + // 1. If fetchParams is canceled, then abort these steps. + if (isCancelled(fetchParams)) { + return + } + + // 2. If fetchParams’s process request end-of-body is non-null, + // then run fetchParams’s process request end-of-body. + if (fetchParams.processRequestEndOfBody) { + fetchParams.processRequestEndOfBody() + } + } + + // 3. Let processBodyError given e be these steps: + const processBodyError = (e) => { + // 1. If fetchParams is canceled, then abort these steps. + if (isCancelled(fetchParams)) { + return + } + + // 2. If e is an "AbortError" DOMException, then abort fetchParams’s controller. + if (e.name === 'AbortError') { + fetchParams.controller.abort() + } else { + fetchParams.controller.terminate(e) + } + } + + // 4. Incrementally read request’s body given processBodyChunk, processEndOfBody, + // processBodyError, and fetchParams’s task destination. + requestBody = (async function * () { + try { + for await (const bytes of request.body.stream) { + yield * processBodyChunk(bytes) + } + processEndOfBody() + } catch (err) { + processBodyError(err) + } + })() + } + + try { + // socket is only provided for websockets + const { body, status, statusText, headersList, socket } = await dispatch({ body: requestBody }) + + if (socket) { + response = makeResponse({ status, statusText, headersList, socket }) + } else { + const iterator = body[Symbol.asyncIterator]() + fetchParams.controller.next = () => iterator.next() + + response = makeResponse({ status, statusText, headersList }) + } + } catch (err) { + // 10. If aborted, then: + if (err.name === 'AbortError') { + // 1. If connection uses HTTP/2, then transmit an RST_STREAM frame. + fetchParams.controller.connection.destroy() + + // 2. Return the appropriate network error for fetchParams. + return makeAppropriateNetworkError(fetchParams, err) + } + + return makeNetworkError(err) + } + + // 11. Let pullAlgorithm be an action that resumes the ongoing fetch + // if it is suspended. + const pullAlgorithm = () => { + fetchParams.controller.resume() + } + + // 12. Let cancelAlgorithm be an algorithm that aborts fetchParams’s + // controller with reason, given reason. + const cancelAlgorithm = (reason) => { + fetchParams.controller.abort(reason) + } + + // 13. Let highWaterMark be a non-negative, non-NaN number, chosen by + // the user agent. + // TODO + + // 14. Let sizeAlgorithm be an algorithm that accepts a chunk object + // and returns a non-negative, non-NaN, non-infinite number, chosen by the user agent. + // TODO + + // 15. Let stream be a new ReadableStream. + // 16. Set up stream with pullAlgorithm set to pullAlgorithm, + // cancelAlgorithm set to cancelAlgorithm, highWaterMark set to + // highWaterMark, and sizeAlgorithm set to sizeAlgorithm. + if (!ReadableStream) { + ReadableStream = (__nccwpck_require__(5356).ReadableStream) + } + + const stream = new ReadableStream( + { + async start (controller) { + fetchParams.controller.controller = controller + }, + async pull (controller) { + await pullAlgorithm(controller) + }, + async cancel (reason) { + await cancelAlgorithm(reason) + } + }, + { + highWaterMark: 0, + size () { + return 1 + } + } + ) + + // 17. Run these steps, but abort when the ongoing fetch is terminated: + + // 1. Set response’s body to a new body whose stream is stream. + response.body = { stream } + + // 2. If response is not a network error and request’s cache mode is + // not "no-store", then update response in httpCache for request. + // TODO + + // 3. If includeCredentials is true and the user agent is not configured + // to block cookies for request (see section 7 of [COOKIES]), then run the + // "set-cookie-string" parsing algorithm (see section 5.2 of [COOKIES]) on + // the value of each header whose name is a byte-case-insensitive match for + // `Set-Cookie` in response’s header list, if any, and request’s current URL. + // TODO + + // 18. If aborted, then: + // TODO + + // 19. Run these steps in parallel: + + // 1. Run these steps, but abort when fetchParams is canceled: + fetchParams.controller.on('terminated', onAborted) + fetchParams.controller.resume = async () => { + // 1. While true + while (true) { + // 1-3. See onData... + + // 4. Set bytes to the result of handling content codings given + // codings and bytes. + let bytes + let isFailure + try { + const { done, value } = await fetchParams.controller.next() + + if (isAborted(fetchParams)) { + break + } + + bytes = done ? undefined : value + } catch (err) { + if (fetchParams.controller.ended && !timingInfo.encodedBodySize) { + // zlib doesn't like empty streams. + bytes = undefined + } else { + bytes = err + + // err may be propagated from the result of calling readablestream.cancel, + // which might not be an error. https://github.com/nodejs/undici/issues/2009 + isFailure = true + } + } + + if (bytes === undefined) { + // 2. Otherwise, if the bytes transmission for response’s message + // body is done normally and stream is readable, then close + // stream, finalize response for fetchParams and response, and + // abort these in-parallel steps. + readableStreamClose(fetchParams.controller.controller) + + finalizeResponse(fetchParams, response) + + return + } + + // 5. Increase timingInfo’s decoded body size by bytes’s length. + timingInfo.decodedBodySize += bytes?.byteLength ?? 0 + + // 6. If bytes is failure, then terminate fetchParams’s controller. + if (isFailure) { + fetchParams.controller.terminate(bytes) + return + } + + // 7. Enqueue a Uint8Array wrapping an ArrayBuffer containing bytes + // into stream. + fetchParams.controller.controller.enqueue(new Uint8Array(bytes)) + + // 8. If stream is errored, then terminate the ongoing fetch. + if (isErrored(stream)) { + fetchParams.controller.terminate() + return + } + + // 9. If stream doesn’t need more data ask the user agent to suspend + // the ongoing fetch. + if (!fetchParams.controller.controller.desiredSize) { + return + } + } + } + + // 2. If aborted, then: + function onAborted (reason) { + // 2. If fetchParams is aborted, then: + if (isAborted(fetchParams)) { + // 1. Set response’s aborted flag. + response.aborted = true + + // 2. If stream is readable, then error stream with the result of + // deserialize a serialized abort reason given fetchParams’s + // controller’s serialized abort reason and an + // implementation-defined realm. + if (isReadable(stream)) { + fetchParams.controller.controller.error( + fetchParams.controller.serializedAbortReason + ) + } + } else { + // 3. Otherwise, if stream is readable, error stream with a TypeError. + if (isReadable(stream)) { + fetchParams.controller.controller.error(new TypeError('terminated', { + cause: isErrorLike(reason) ? reason : undefined + })) + } + } + + // 4. If connection uses HTTP/2, then transmit an RST_STREAM frame. + // 5. Otherwise, the user agent should close connection unless it would be bad for performance to do so. + fetchParams.controller.connection.destroy() + } + + // 20. Return response. + return response + + async function dispatch ({ body }) { + const url = requestCurrentURL(request) + /** @type {import('../..').Agent} */ + const agent = fetchParams.controller.dispatcher + + return new Promise((resolve, reject) => agent.dispatch( + { + path: url.pathname + url.search, + origin: url.origin, + method: request.method, + body: fetchParams.controller.dispatcher.isMockActive ? request.body && request.body.source : body, + headers: request.headersList.entries, + maxRedirections: 0, + upgrade: request.mode === 'websocket' ? 'websocket' : undefined + }, + { + body: null, + abort: null, + + onConnect (abort) { + // TODO (fix): Do we need connection here? + const { connection } = fetchParams.controller + + if (connection.destroyed) { + abort(new DOMException('The operation was aborted.', 'AbortError')) + } else { + fetchParams.controller.on('terminated', abort) + this.abort = connection.abort = abort + } + }, + + onHeaders (status, headersList, resume, statusText) { + if (status < 200) { + return + } + + let codings = [] + let location = '' + + const headers = new Headers() + + // For H2, the headers are a plain JS object + // We distinguish between them and iterate accordingly + if (Array.isArray(headersList)) { + for (let n = 0; n < headersList.length; n += 2) { + const key = headersList[n + 0].toString('latin1') + const val = headersList[n + 1].toString('latin1') + if (key.toLowerCase() === 'content-encoding') { + // https://www.rfc-editor.org/rfc/rfc7231#section-3.1.2.1 + // "All content-coding values are case-insensitive..." + codings = val.toLowerCase().split(',').map((x) => x.trim()) + } else if (key.toLowerCase() === 'location') { + location = val + } + + headers.append(key, val) + } + } else { + const keys = Object.keys(headersList) + for (const key of keys) { + const val = headersList[key] + if (key.toLowerCase() === 'content-encoding') { + // https://www.rfc-editor.org/rfc/rfc7231#section-3.1.2.1 + // "All content-coding values are case-insensitive..." + codings = val.toLowerCase().split(',').map((x) => x.trim()).reverse() + } else if (key.toLowerCase() === 'location') { + location = val + } + + headers.append(key, val) + } + } + + this.body = new Readable({ read: resume }) + + const decoders = [] + + const willFollow = request.redirect === 'follow' && + location && + redirectStatusSet.has(status) + + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Encoding + if (request.method !== 'HEAD' && request.method !== 'CONNECT' && !nullBodyStatus.includes(status) && !willFollow) { + for (const coding of codings) { + // https://www.rfc-editor.org/rfc/rfc9112.html#section-7.2 + if (coding === 'x-gzip' || coding === 'gzip') { + decoders.push(zlib.createGunzip({ + // Be less strict when decoding compressed responses, since sometimes + // servers send slightly invalid responses that are still accepted + // by common browsers. + // Always using Z_SYNC_FLUSH is what cURL does. + flush: zlib.constants.Z_SYNC_FLUSH, + finishFlush: zlib.constants.Z_SYNC_FLUSH + })) + } else if (coding === 'deflate') { + decoders.push(zlib.createInflate()) + } else if (coding === 'br') { + decoders.push(zlib.createBrotliDecompress()) + } else { + decoders.length = 0 + break + } + } + } + + resolve({ + status, + statusText, + headersList: headers[kHeadersList], + body: decoders.length + ? pipeline(this.body, ...decoders, () => { }) + : this.body.on('error', () => {}) + }) + + return true + }, + + onData (chunk) { + if (fetchParams.controller.dump) { + return + } + + // 1. If one or more bytes have been transmitted from response’s + // message body, then: + + // 1. Let bytes be the transmitted bytes. + const bytes = chunk + + // 2. Let codings be the result of extracting header list values + // given `Content-Encoding` and response’s header list. + // See pullAlgorithm. + + // 3. Increase timingInfo’s encoded body size by bytes’s length. + timingInfo.encodedBodySize += bytes.byteLength + + // 4. See pullAlgorithm... + + return this.body.push(bytes) + }, + + onComplete () { + if (this.abort) { + fetchParams.controller.off('terminated', this.abort) + } + + fetchParams.controller.ended = true + + this.body.push(null) + }, + + onError (error) { + if (this.abort) { + fetchParams.controller.off('terminated', this.abort) + } + + this.body?.destroy(error) + + fetchParams.controller.terminate(error) + + reject(error) + }, + + onUpgrade (status, headersList, socket) { + if (status !== 101) { + return + } + + const headers = new Headers() + + for (let n = 0; n < headersList.length; n += 2) { + const key = headersList[n + 0].toString('latin1') + const val = headersList[n + 1].toString('latin1') + + headers.append(key, val) + } + + resolve({ + status, + statusText: STATUS_CODES[status], + headersList: headers[kHeadersList], + socket + }) + + return true + } + } + )) + } +} + +module.exports = { + fetch, + Fetch, + fetching, + finalizeAndReportTiming +} + + +/***/ }), + +/***/ 8359: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; +/* globals AbortController */ + + + +const { extractBody, mixinBody, cloneBody } = __nccwpck_require__(1472) +const { Headers, fill: fillHeaders, HeadersList } = __nccwpck_require__(554) +const { FinalizationRegistry } = __nccwpck_require__(6436)() +const util = __nccwpck_require__(3983) +const { + isValidHTTPToken, + sameOrigin, + normalizeMethod, + makePolicyContainer +} = __nccwpck_require__(2538) +const { + forbiddenMethodsSet, + corsSafeListedMethodsSet, + referrerPolicy, + requestRedirect, + requestMode, + requestCredentials, + requestCache, + requestDuplex +} = __nccwpck_require__(1037) +const { kEnumerableProperty } = util +const { kHeaders, kSignal, kState, kGuard, kRealm } = __nccwpck_require__(5861) +const { webidl } = __nccwpck_require__(1744) +const { getGlobalOrigin } = __nccwpck_require__(1246) +const { URLSerializer } = __nccwpck_require__(685) +const { kHeadersList } = __nccwpck_require__(2785) +const assert = __nccwpck_require__(9491) +const { getMaxListeners, setMaxListeners, getEventListeners, defaultMaxListeners } = __nccwpck_require__(2361) + +let TransformStream = globalThis.TransformStream + +const kInit = Symbol('init') +const kAbortController = Symbol('abortController') + +const requestFinalizer = new FinalizationRegistry(({ signal, abort }) => { + signal.removeEventListener('abort', abort) +}) + +// https://fetch.spec.whatwg.org/#request-class +class Request { + // https://fetch.spec.whatwg.org/#dom-request + constructor (input, init = {}) { + if (input === kInit) { + return + } + + webidl.argumentLengthCheck(arguments, 1, { header: 'Request constructor' }) + + input = webidl.converters.RequestInfo(input) + init = webidl.converters.RequestInit(init) + + // https://html.spec.whatwg.org/multipage/webappapis.html#environment-settings-object + this[kRealm] = { + settingsObject: { + baseUrl: getGlobalOrigin(), + get origin () { + return this.baseUrl?.origin + }, + policyContainer: makePolicyContainer() + } + } + + // 1. Let request be null. + let request = null + + // 2. Let fallbackMode be null. + let fallbackMode = null + + // 3. Let baseURL be this’s relevant settings object’s API base URL. + const baseUrl = this[kRealm].settingsObject.baseUrl + + // 4. Let signal be null. + let signal = null + + // 5. If input is a string, then: + if (typeof input === 'string') { + // 1. Let parsedURL be the result of parsing input with baseURL. + // 2. If parsedURL is failure, then throw a TypeError. + let parsedURL + try { + parsedURL = new URL(input, baseUrl) + } catch (err) { + throw new TypeError('Failed to parse URL from ' + input, { cause: err }) + } + + // 3. If parsedURL includes credentials, then throw a TypeError. + if (parsedURL.username || parsedURL.password) { + throw new TypeError( + 'Request cannot be constructed from a URL that includes credentials: ' + + input + ) + } + + // 4. Set request to a new request whose URL is parsedURL. + request = makeRequest({ urlList: [parsedURL] }) + + // 5. Set fallbackMode to "cors". + fallbackMode = 'cors' + } else { + // 6. Otherwise: + + // 7. Assert: input is a Request object. + assert(input instanceof Request) + + // 8. Set request to input’s request. + request = input[kState] + + // 9. Set signal to input’s signal. + signal = input[kSignal] + } + + // 7. Let origin be this’s relevant settings object’s origin. + const origin = this[kRealm].settingsObject.origin + + // 8. Let window be "client". + let window = 'client' + + // 9. If request’s window is an environment settings object and its origin + // is same origin with origin, then set window to request’s window. + if ( + request.window?.constructor?.name === 'EnvironmentSettingsObject' && + sameOrigin(request.window, origin) + ) { + window = request.window + } + + // 10. If init["window"] exists and is non-null, then throw a TypeError. + if (init.window != null) { + throw new TypeError(`'window' option '${window}' must be null`) + } + + // 11. If init["window"] exists, then set window to "no-window". + if ('window' in init) { + window = 'no-window' + } + + // 12. Set request to a new request with the following properties: + request = makeRequest({ + // URL request’s URL. + // undici implementation note: this is set as the first item in request's urlList in makeRequest + // method request’s method. + method: request.method, + // header list A copy of request’s header list. + // undici implementation note: headersList is cloned in makeRequest + headersList: request.headersList, + // unsafe-request flag Set. + unsafeRequest: request.unsafeRequest, + // client This’s relevant settings object. + client: this[kRealm].settingsObject, + // window window. + window, + // priority request’s priority. + priority: request.priority, + // origin request’s origin. The propagation of the origin is only significant for navigation requests + // being handled by a service worker. In this scenario a request can have an origin that is different + // from the current client. + origin: request.origin, + // referrer request’s referrer. + referrer: request.referrer, + // referrer policy request’s referrer policy. + referrerPolicy: request.referrerPolicy, + // mode request’s mode. + mode: request.mode, + // credentials mode request’s credentials mode. + credentials: request.credentials, + // cache mode request’s cache mode. + cache: request.cache, + // redirect mode request’s redirect mode. + redirect: request.redirect, + // integrity metadata request’s integrity metadata. + integrity: request.integrity, + // keepalive request’s keepalive. + keepalive: request.keepalive, + // reload-navigation flag request’s reload-navigation flag. + reloadNavigation: request.reloadNavigation, + // history-navigation flag request’s history-navigation flag. + historyNavigation: request.historyNavigation, + // URL list A clone of request’s URL list. + urlList: [...request.urlList] + }) + + // 13. If init is not empty, then: + if (Object.keys(init).length > 0) { + // 1. If request’s mode is "navigate", then set it to "same-origin". + if (request.mode === 'navigate') { + request.mode = 'same-origin' + } + + // 2. Unset request’s reload-navigation flag. + request.reloadNavigation = false + + // 3. Unset request’s history-navigation flag. + request.historyNavigation = false + + // 4. Set request’s origin to "client". + request.origin = 'client' + + // 5. Set request’s referrer to "client" + request.referrer = 'client' + + // 6. Set request’s referrer policy to the empty string. + request.referrerPolicy = '' + + // 7. Set request’s URL to request’s current URL. + request.url = request.urlList[request.urlList.length - 1] + + // 8. Set request’s URL list to « request’s URL ». + request.urlList = [request.url] + } + + // 14. If init["referrer"] exists, then: + if (init.referrer !== undefined) { + // 1. Let referrer be init["referrer"]. + const referrer = init.referrer + + // 2. If referrer is the empty string, then set request’s referrer to "no-referrer". + if (referrer === '') { + request.referrer = 'no-referrer' + } else { + // 1. Let parsedReferrer be the result of parsing referrer with + // baseURL. + // 2. If parsedReferrer is failure, then throw a TypeError. + let parsedReferrer + try { + parsedReferrer = new URL(referrer, baseUrl) + } catch (err) { + throw new TypeError(`Referrer "${referrer}" is not a valid URL.`, { cause: err }) + } + + // 3. If one of the following is true + // - parsedReferrer’s scheme is "about" and path is the string "client" + // - parsedReferrer’s origin is not same origin with origin + // then set request’s referrer to "client". + if ( + (parsedReferrer.protocol === 'about:' && parsedReferrer.hostname === 'client') || + (origin && !sameOrigin(parsedReferrer, this[kRealm].settingsObject.baseUrl)) + ) { + request.referrer = 'client' + } else { + // 4. Otherwise, set request’s referrer to parsedReferrer. + request.referrer = parsedReferrer + } + } + } + + // 15. If init["referrerPolicy"] exists, then set request’s referrer policy + // to it. + if (init.referrerPolicy !== undefined) { + request.referrerPolicy = init.referrerPolicy + } + + // 16. Let mode be init["mode"] if it exists, and fallbackMode otherwise. + let mode + if (init.mode !== undefined) { + mode = init.mode + } else { + mode = fallbackMode + } + + // 17. If mode is "navigate", then throw a TypeError. + if (mode === 'navigate') { + throw webidl.errors.exception({ + header: 'Request constructor', + message: 'invalid request mode navigate.' + }) + } + + // 18. If mode is non-null, set request’s mode to mode. + if (mode != null) { + request.mode = mode + } + + // 19. If init["credentials"] exists, then set request’s credentials mode + // to it. + if (init.credentials !== undefined) { + request.credentials = init.credentials + } + + // 18. If init["cache"] exists, then set request’s cache mode to it. + if (init.cache !== undefined) { + request.cache = init.cache + } + + // 21. If request’s cache mode is "only-if-cached" and request’s mode is + // not "same-origin", then throw a TypeError. + if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') { + throw new TypeError( + "'only-if-cached' can be set only with 'same-origin' mode" + ) + } + + // 22. If init["redirect"] exists, then set request’s redirect mode to it. + if (init.redirect !== undefined) { + request.redirect = init.redirect + } + + // 23. If init["integrity"] exists, then set request’s integrity metadata to it. + if (init.integrity !== undefined && init.integrity != null) { + request.integrity = String(init.integrity) + } + + // 24. If init["keepalive"] exists, then set request’s keepalive to it. + if (init.keepalive !== undefined) { + request.keepalive = Boolean(init.keepalive) + } + + // 25. If init["method"] exists, then: + if (init.method !== undefined) { + // 1. Let method be init["method"]. + let method = init.method + + // 2. If method is not a method or method is a forbidden method, then + // throw a TypeError. + if (!isValidHTTPToken(init.method)) { + throw TypeError(`'${init.method}' is not a valid HTTP method.`) + } + + if (forbiddenMethodsSet.has(method.toUpperCase())) { + throw TypeError(`'${init.method}' HTTP method is unsupported.`) + } + + // 3. Normalize method. + method = normalizeMethod(init.method) + + // 4. Set request’s method to method. + request.method = method + } + + // 26. If init["signal"] exists, then set signal to it. + if (init.signal !== undefined) { + signal = init.signal + } + + // 27. Set this’s request to request. + this[kState] = request + + // 28. Set this’s signal to a new AbortSignal object with this’s relevant + // Realm. + // TODO: could this be simplified with AbortSignal.any + // (https://dom.spec.whatwg.org/#dom-abortsignal-any) + const ac = new AbortController() + this[kSignal] = ac.signal + this[kSignal][kRealm] = this[kRealm] + + // 29. If signal is not null, then make this’s signal follow signal. + if (signal != null) { + if ( + !signal || + typeof signal.aborted !== 'boolean' || + typeof signal.addEventListener !== 'function' + ) { + throw new TypeError( + "Failed to construct 'Request': member signal is not of type AbortSignal." + ) + } + + if (signal.aborted) { + ac.abort(signal.reason) + } else { + // Keep a strong ref to ac while request object + // is alive. This is needed to prevent AbortController + // from being prematurely garbage collected. + // See, https://github.com/nodejs/undici/issues/1926. + this[kAbortController] = ac + + const acRef = new WeakRef(ac) + const abort = function () { + const ac = acRef.deref() + if (ac !== undefined) { + ac.abort(this.reason) + } + } + + // Third-party AbortControllers may not work with these. + // See, https://github.com/nodejs/undici/pull/1910#issuecomment-1464495619. + try { + // If the max amount of listeners is equal to the default, increase it + // This is only available in node >= v19.9.0 + if (typeof getMaxListeners === 'function' && getMaxListeners(signal) === defaultMaxListeners) { + setMaxListeners(100, signal) + } else if (getEventListeners(signal, 'abort').length >= defaultMaxListeners) { + setMaxListeners(100, signal) + } + } catch {} + + util.addAbortListener(signal, abort) + requestFinalizer.register(ac, { signal, abort }) + } + } + + // 30. Set this’s headers to a new Headers object with this’s relevant + // Realm, whose header list is request’s header list and guard is + // "request". + this[kHeaders] = new Headers() + this[kHeaders][kHeadersList] = request.headersList + this[kHeaders][kGuard] = 'request' + this[kHeaders][kRealm] = this[kRealm] + + // 31. If this’s request’s mode is "no-cors", then: + if (mode === 'no-cors') { + // 1. If this’s request’s method is not a CORS-safelisted method, + // then throw a TypeError. + if (!corsSafeListedMethodsSet.has(request.method)) { + throw new TypeError( + `'${request.method} is unsupported in no-cors mode.` + ) + } + + // 2. Set this’s headers’s guard to "request-no-cors". + this[kHeaders][kGuard] = 'request-no-cors' + } + + // 32. If init is not empty, then: + if (Object.keys(init).length !== 0) { + // 1. Let headers be a copy of this’s headers and its associated header + // list. + let headers = new Headers(this[kHeaders]) + + // 2. If init["headers"] exists, then set headers to init["headers"]. + if (init.headers !== undefined) { + headers = init.headers + } + + // 3. Empty this’s headers’s header list. + this[kHeaders][kHeadersList].clear() + + // 4. If headers is a Headers object, then for each header in its header + // list, append header’s name/header’s value to this’s headers. + if (headers.constructor.name === 'Headers') { + for (const [key, val] of headers) { + this[kHeaders].append(key, val) + } + } else { + // 5. Otherwise, fill this’s headers with headers. + fillHeaders(this[kHeaders], headers) + } + } + + // 33. Let inputBody be input’s request’s body if input is a Request + // object; otherwise null. + const inputBody = input instanceof Request ? input[kState].body : null + + // 34. If either init["body"] exists and is non-null or inputBody is + // non-null, and request’s method is `GET` or `HEAD`, then throw a + // TypeError. + if ( + (init.body != null || inputBody != null) && + (request.method === 'GET' || request.method === 'HEAD') + ) { + throw new TypeError('Request with GET/HEAD method cannot have body.') + } + + // 35. Let initBody be null. + let initBody = null + + // 36. If init["body"] exists and is non-null, then: + if (init.body != null) { + // 1. Let Content-Type be null. + // 2. Set initBody and Content-Type to the result of extracting + // init["body"], with keepalive set to request’s keepalive. + const [extractedBody, contentType] = extractBody( + init.body, + request.keepalive + ) + initBody = extractedBody + + // 3, If Content-Type is non-null and this’s headers’s header list does + // not contain `Content-Type`, then append `Content-Type`/Content-Type to + // this’s headers. + if (contentType && !this[kHeaders][kHeadersList].contains('content-type')) { + this[kHeaders].append('content-type', contentType) + } + } + + // 37. Let inputOrInitBody be initBody if it is non-null; otherwise + // inputBody. + const inputOrInitBody = initBody ?? inputBody + + // 38. If inputOrInitBody is non-null and inputOrInitBody’s source is + // null, then: + if (inputOrInitBody != null && inputOrInitBody.source == null) { + // 1. If initBody is non-null and init["duplex"] does not exist, + // then throw a TypeError. + if (initBody != null && init.duplex == null) { + throw new TypeError('RequestInit: duplex option is required when sending a body.') + } + + // 2. If this’s request’s mode is neither "same-origin" nor "cors", + // then throw a TypeError. + if (request.mode !== 'same-origin' && request.mode !== 'cors') { + throw new TypeError( + 'If request is made from ReadableStream, mode should be "same-origin" or "cors"' + ) + } + + // 3. Set this’s request’s use-CORS-preflight flag. + request.useCORSPreflightFlag = true + } + + // 39. Let finalBody be inputOrInitBody. + let finalBody = inputOrInitBody + + // 40. If initBody is null and inputBody is non-null, then: + if (initBody == null && inputBody != null) { + // 1. If input is unusable, then throw a TypeError. + if (util.isDisturbed(inputBody.stream) || inputBody.stream.locked) { + throw new TypeError( + 'Cannot construct a Request with a Request object that has already been used.' + ) + } + + // 2. Set finalBody to the result of creating a proxy for inputBody. + if (!TransformStream) { + TransformStream = (__nccwpck_require__(5356).TransformStream) + } + + // https://streams.spec.whatwg.org/#readablestream-create-a-proxy + const identityTransform = new TransformStream() + inputBody.stream.pipeThrough(identityTransform) + finalBody = { + source: inputBody.source, + length: inputBody.length, + stream: identityTransform.readable + } + } + + // 41. Set this’s request’s body to finalBody. + this[kState].body = finalBody + } + + // Returns request’s HTTP method, which is "GET" by default. + get method () { + webidl.brandCheck(this, Request) + + // The method getter steps are to return this’s request’s method. + return this[kState].method + } + + // Returns the URL of request as a string. + get url () { + webidl.brandCheck(this, Request) + + // The url getter steps are to return this’s request’s URL, serialized. + return URLSerializer(this[kState].url) + } + + // Returns a Headers object consisting of the headers associated with request. + // Note that headers added in the network layer by the user agent will not + // be accounted for in this object, e.g., the "Host" header. + get headers () { + webidl.brandCheck(this, Request) + + // The headers getter steps are to return this’s headers. + return this[kHeaders] + } + + // Returns the kind of resource requested by request, e.g., "document" + // or "script". + get destination () { + webidl.brandCheck(this, Request) + + // The destination getter are to return this’s request’s destination. + return this[kState].destination + } + + // Returns the referrer of request. Its value can be a same-origin URL if + // explicitly set in init, the empty string to indicate no referrer, and + // "about:client" when defaulting to the global’s default. This is used + // during fetching to determine the value of the `Referer` header of the + // request being made. + get referrer () { + webidl.brandCheck(this, Request) + + // 1. If this’s request’s referrer is "no-referrer", then return the + // empty string. + if (this[kState].referrer === 'no-referrer') { + return '' + } + + // 2. If this’s request’s referrer is "client", then return + // "about:client". + if (this[kState].referrer === 'client') { + return 'about:client' + } + + // Return this’s request’s referrer, serialized. + return this[kState].referrer.toString() + } + + // Returns the referrer policy associated with request. + // This is used during fetching to compute the value of the request’s + // referrer. + get referrerPolicy () { + webidl.brandCheck(this, Request) + + // The referrerPolicy getter steps are to return this’s request’s referrer policy. + return this[kState].referrerPolicy + } + + // Returns the mode associated with request, which is a string indicating + // whether the request will use CORS, or will be restricted to same-origin + // URLs. + get mode () { + webidl.brandCheck(this, Request) + + // The mode getter steps are to return this’s request’s mode. + return this[kState].mode + } + + // Returns the credentials mode associated with request, + // which is a string indicating whether credentials will be sent with the + // request always, never, or only when sent to a same-origin URL. + get credentials () { + // The credentials getter steps are to return this’s request’s credentials mode. + return this[kState].credentials + } + + // Returns the cache mode associated with request, + // which is a string indicating how the request will + // interact with the browser’s cache when fetching. + get cache () { + webidl.brandCheck(this, Request) + + // The cache getter steps are to return this’s request’s cache mode. + return this[kState].cache + } + + // Returns the redirect mode associated with request, + // which is a string indicating how redirects for the + // request will be handled during fetching. A request + // will follow redirects by default. + get redirect () { + webidl.brandCheck(this, Request) + + // The redirect getter steps are to return this’s request’s redirect mode. + return this[kState].redirect + } + + // Returns request’s subresource integrity metadata, which is a + // cryptographic hash of the resource being fetched. Its value + // consists of multiple hashes separated by whitespace. [SRI] + get integrity () { + webidl.brandCheck(this, Request) + + // The integrity getter steps are to return this’s request’s integrity + // metadata. + return this[kState].integrity + } + + // Returns a boolean indicating whether or not request can outlive the + // global in which it was created. + get keepalive () { + webidl.brandCheck(this, Request) + + // The keepalive getter steps are to return this’s request’s keepalive. + return this[kState].keepalive + } + + // Returns a boolean indicating whether or not request is for a reload + // navigation. + get isReloadNavigation () { + webidl.brandCheck(this, Request) + + // The isReloadNavigation getter steps are to return true if this’s + // request’s reload-navigation flag is set; otherwise false. + return this[kState].reloadNavigation + } + + // Returns a boolean indicating whether or not request is for a history + // navigation (a.k.a. back-foward navigation). + get isHistoryNavigation () { + webidl.brandCheck(this, Request) + + // The isHistoryNavigation getter steps are to return true if this’s request’s + // history-navigation flag is set; otherwise false. + return this[kState].historyNavigation + } + + // Returns the signal associated with request, which is an AbortSignal + // object indicating whether or not request has been aborted, and its + // abort event handler. + get signal () { + webidl.brandCheck(this, Request) + + // The signal getter steps are to return this’s signal. + return this[kSignal] + } + + get body () { + webidl.brandCheck(this, Request) + + return this[kState].body ? this[kState].body.stream : null + } + + get bodyUsed () { + webidl.brandCheck(this, Request) + + return !!this[kState].body && util.isDisturbed(this[kState].body.stream) + } + + get duplex () { + webidl.brandCheck(this, Request) + + return 'half' + } + + // Returns a clone of request. + clone () { + webidl.brandCheck(this, Request) + + // 1. If this is unusable, then throw a TypeError. + if (this.bodyUsed || this.body?.locked) { + throw new TypeError('unusable') + } + + // 2. Let clonedRequest be the result of cloning this’s request. + const clonedRequest = cloneRequest(this[kState]) + + // 3. Let clonedRequestObject be the result of creating a Request object, + // given clonedRequest, this’s headers’s guard, and this’s relevant Realm. + const clonedRequestObject = new Request(kInit) + clonedRequestObject[kState] = clonedRequest + clonedRequestObject[kRealm] = this[kRealm] + clonedRequestObject[kHeaders] = new Headers() + clonedRequestObject[kHeaders][kHeadersList] = clonedRequest.headersList + clonedRequestObject[kHeaders][kGuard] = this[kHeaders][kGuard] + clonedRequestObject[kHeaders][kRealm] = this[kHeaders][kRealm] + + // 4. Make clonedRequestObject’s signal follow this’s signal. + const ac = new AbortController() + if (this.signal.aborted) { + ac.abort(this.signal.reason) + } else { + util.addAbortListener( + this.signal, + () => { + ac.abort(this.signal.reason) + } + ) + } + clonedRequestObject[kSignal] = ac.signal + + // 4. Return clonedRequestObject. + return clonedRequestObject + } +} + +mixinBody(Request) + +function makeRequest (init) { + // https://fetch.spec.whatwg.org/#requests + const request = { + method: 'GET', + localURLsOnly: false, + unsafeRequest: false, + body: null, + client: null, + reservedClient: null, + replacesClientId: '', + window: 'client', + keepalive: false, + serviceWorkers: 'all', + initiator: '', + destination: '', + priority: null, + origin: 'client', + policyContainer: 'client', + referrer: 'client', + referrerPolicy: '', + mode: 'no-cors', + useCORSPreflightFlag: false, + credentials: 'same-origin', + useCredentials: false, + cache: 'default', + redirect: 'follow', + integrity: '', + cryptoGraphicsNonceMetadata: '', + parserMetadata: '', + reloadNavigation: false, + historyNavigation: false, + userActivation: false, + taintedOrigin: false, + redirectCount: 0, + responseTainting: 'basic', + preventNoCacheCacheControlHeaderModification: false, + done: false, + timingAllowFailed: false, + ...init, + headersList: init.headersList + ? new HeadersList(init.headersList) + : new HeadersList() + } + request.url = request.urlList[0] + return request +} + +// https://fetch.spec.whatwg.org/#concept-request-clone +function cloneRequest (request) { + // To clone a request request, run these steps: + + // 1. Let newRequest be a copy of request, except for its body. + const newRequest = makeRequest({ ...request, body: null }) + + // 2. If request’s body is non-null, set newRequest’s body to the + // result of cloning request’s body. + if (request.body != null) { + newRequest.body = cloneBody(request.body) + } + + // 3. Return newRequest. + return newRequest +} + +Object.defineProperties(Request.prototype, { + method: kEnumerableProperty, + url: kEnumerableProperty, + headers: kEnumerableProperty, + redirect: kEnumerableProperty, + clone: kEnumerableProperty, + signal: kEnumerableProperty, + duplex: kEnumerableProperty, + destination: kEnumerableProperty, + body: kEnumerableProperty, + bodyUsed: kEnumerableProperty, + isHistoryNavigation: kEnumerableProperty, + isReloadNavigation: kEnumerableProperty, + keepalive: kEnumerableProperty, + integrity: kEnumerableProperty, + cache: kEnumerableProperty, + credentials: kEnumerableProperty, + attribute: kEnumerableProperty, + referrerPolicy: kEnumerableProperty, + referrer: kEnumerableProperty, + mode: kEnumerableProperty, + [Symbol.toStringTag]: { + value: 'Request', + configurable: true + } +}) + +webidl.converters.Request = webidl.interfaceConverter( + Request +) + +// https://fetch.spec.whatwg.org/#requestinfo +webidl.converters.RequestInfo = function (V) { + if (typeof V === 'string') { + return webidl.converters.USVString(V) + } + + if (V instanceof Request) { + return webidl.converters.Request(V) + } + + return webidl.converters.USVString(V) +} + +webidl.converters.AbortSignal = webidl.interfaceConverter( + AbortSignal +) + +// https://fetch.spec.whatwg.org/#requestinit +webidl.converters.RequestInit = webidl.dictionaryConverter([ + { + key: 'method', + converter: webidl.converters.ByteString + }, + { + key: 'headers', + converter: webidl.converters.HeadersInit + }, + { + key: 'body', + converter: webidl.nullableConverter( + webidl.converters.BodyInit + ) + }, + { + key: 'referrer', + converter: webidl.converters.USVString + }, + { + key: 'referrerPolicy', + converter: webidl.converters.DOMString, + // https://w3c.github.io/webappsec-referrer-policy/#referrer-policy + allowedValues: referrerPolicy + }, + { + key: 'mode', + converter: webidl.converters.DOMString, + // https://fetch.spec.whatwg.org/#concept-request-mode + allowedValues: requestMode + }, + { + key: 'credentials', + converter: webidl.converters.DOMString, + // https://fetch.spec.whatwg.org/#requestcredentials + allowedValues: requestCredentials + }, + { + key: 'cache', + converter: webidl.converters.DOMString, + // https://fetch.spec.whatwg.org/#requestcache + allowedValues: requestCache + }, + { + key: 'redirect', + converter: webidl.converters.DOMString, + // https://fetch.spec.whatwg.org/#requestredirect + allowedValues: requestRedirect + }, + { + key: 'integrity', + converter: webidl.converters.DOMString + }, + { + key: 'keepalive', + converter: webidl.converters.boolean + }, + { + key: 'signal', + converter: webidl.nullableConverter( + (signal) => webidl.converters.AbortSignal( + signal, + { strict: false } + ) + ) + }, + { + key: 'window', + converter: webidl.converters.any + }, + { + key: 'duplex', + converter: webidl.converters.DOMString, + allowedValues: requestDuplex + } +]) + +module.exports = { Request, makeRequest } + + +/***/ }), + +/***/ 7823: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { Headers, HeadersList, fill } = __nccwpck_require__(554) +const { extractBody, cloneBody, mixinBody } = __nccwpck_require__(1472) +const util = __nccwpck_require__(3983) +const { kEnumerableProperty } = util +const { + isValidReasonPhrase, + isCancelled, + isAborted, + isBlobLike, + serializeJavascriptValueToJSONString, + isErrorLike, + isomorphicEncode +} = __nccwpck_require__(2538) +const { + redirectStatusSet, + nullBodyStatus, + DOMException +} = __nccwpck_require__(1037) +const { kState, kHeaders, kGuard, kRealm } = __nccwpck_require__(5861) +const { webidl } = __nccwpck_require__(1744) +const { FormData } = __nccwpck_require__(2015) +const { getGlobalOrigin } = __nccwpck_require__(1246) +const { URLSerializer } = __nccwpck_require__(685) +const { kHeadersList } = __nccwpck_require__(2785) +const assert = __nccwpck_require__(9491) +const { types } = __nccwpck_require__(3837) + +const ReadableStream = globalThis.ReadableStream || (__nccwpck_require__(5356).ReadableStream) +const textEncoder = new TextEncoder('utf-8') + +// https://fetch.spec.whatwg.org/#response-class +class Response { + // Creates network error Response. + static error () { + // TODO + const relevantRealm = { settingsObject: {} } + + // The static error() method steps are to return the result of creating a + // Response object, given a new network error, "immutable", and this’s + // relevant Realm. + const responseObject = new Response() + responseObject[kState] = makeNetworkError() + responseObject[kRealm] = relevantRealm + responseObject[kHeaders][kHeadersList] = responseObject[kState].headersList + responseObject[kHeaders][kGuard] = 'immutable' + responseObject[kHeaders][kRealm] = relevantRealm + return responseObject + } + + // https://fetch.spec.whatwg.org/#dom-response-json + static json (data, init = {}) { + webidl.argumentLengthCheck(arguments, 1, { header: 'Response.json' }) + + if (init !== null) { + init = webidl.converters.ResponseInit(init) + } + + // 1. Let bytes the result of running serialize a JavaScript value to JSON bytes on data. + const bytes = textEncoder.encode( + serializeJavascriptValueToJSONString(data) + ) + + // 2. Let body be the result of extracting bytes. + const body = extractBody(bytes) + + // 3. Let responseObject be the result of creating a Response object, given a new response, + // "response", and this’s relevant Realm. + const relevantRealm = { settingsObject: {} } + const responseObject = new Response() + responseObject[kRealm] = relevantRealm + responseObject[kHeaders][kGuard] = 'response' + responseObject[kHeaders][kRealm] = relevantRealm + + // 4. Perform initialize a response given responseObject, init, and (body, "application/json"). + initializeResponse(responseObject, init, { body: body[0], type: 'application/json' }) + + // 5. Return responseObject. + return responseObject + } + + // Creates a redirect Response that redirects to url with status status. + static redirect (url, status = 302) { + const relevantRealm = { settingsObject: {} } + + webidl.argumentLengthCheck(arguments, 1, { header: 'Response.redirect' }) + + url = webidl.converters.USVString(url) + status = webidl.converters['unsigned short'](status) + + // 1. Let parsedURL be the result of parsing url with current settings + // object’s API base URL. + // 2. If parsedURL is failure, then throw a TypeError. + // TODO: base-URL? + let parsedURL + try { + parsedURL = new URL(url, getGlobalOrigin()) + } catch (err) { + throw Object.assign(new TypeError('Failed to parse URL from ' + url), { + cause: err + }) + } + + // 3. If status is not a redirect status, then throw a RangeError. + if (!redirectStatusSet.has(status)) { + throw new RangeError('Invalid status code ' + status) + } + + // 4. Let responseObject be the result of creating a Response object, + // given a new response, "immutable", and this’s relevant Realm. + const responseObject = new Response() + responseObject[kRealm] = relevantRealm + responseObject[kHeaders][kGuard] = 'immutable' + responseObject[kHeaders][kRealm] = relevantRealm + + // 5. Set responseObject’s response’s status to status. + responseObject[kState].status = status + + // 6. Let value be parsedURL, serialized and isomorphic encoded. + const value = isomorphicEncode(URLSerializer(parsedURL)) + + // 7. Append `Location`/value to responseObject’s response’s header list. + responseObject[kState].headersList.append('location', value) + + // 8. Return responseObject. + return responseObject + } + + // https://fetch.spec.whatwg.org/#dom-response + constructor (body = null, init = {}) { + if (body !== null) { + body = webidl.converters.BodyInit(body) + } + + init = webidl.converters.ResponseInit(init) + + // TODO + this[kRealm] = { settingsObject: {} } + + // 1. Set this’s response to a new response. + this[kState] = makeResponse({}) + + // 2. Set this’s headers to a new Headers object with this’s relevant + // Realm, whose header list is this’s response’s header list and guard + // is "response". + this[kHeaders] = new Headers() + this[kHeaders][kGuard] = 'response' + this[kHeaders][kHeadersList] = this[kState].headersList + this[kHeaders][kRealm] = this[kRealm] + + // 3. Let bodyWithType be null. + let bodyWithType = null + + // 4. If body is non-null, then set bodyWithType to the result of extracting body. + if (body != null) { + const [extractedBody, type] = extractBody(body) + bodyWithType = { body: extractedBody, type } + } + + // 5. Perform initialize a response given this, init, and bodyWithType. + initializeResponse(this, init, bodyWithType) + } + + // Returns response’s type, e.g., "cors". + get type () { + webidl.brandCheck(this, Response) + + // The type getter steps are to return this’s response’s type. + return this[kState].type + } + + // Returns response’s URL, if it has one; otherwise the empty string. + get url () { + webidl.brandCheck(this, Response) + + const urlList = this[kState].urlList + + // The url getter steps are to return the empty string if this’s + // response’s URL is null; otherwise this’s response’s URL, + // serialized with exclude fragment set to true. + const url = urlList[urlList.length - 1] ?? null + + if (url === null) { + return '' + } + + return URLSerializer(url, true) + } + + // Returns whether response was obtained through a redirect. + get redirected () { + webidl.brandCheck(this, Response) + + // The redirected getter steps are to return true if this’s response’s URL + // list has more than one item; otherwise false. + return this[kState].urlList.length > 1 + } + + // Returns response’s status. + get status () { + webidl.brandCheck(this, Response) + + // The status getter steps are to return this’s response’s status. + return this[kState].status + } + + // Returns whether response’s status is an ok status. + get ok () { + webidl.brandCheck(this, Response) + + // The ok getter steps are to return true if this’s response’s status is an + // ok status; otherwise false. + return this[kState].status >= 200 && this[kState].status <= 299 + } + + // Returns response’s status message. + get statusText () { + webidl.brandCheck(this, Response) + + // The statusText getter steps are to return this’s response’s status + // message. + return this[kState].statusText + } + + // Returns response’s headers as Headers. + get headers () { + webidl.brandCheck(this, Response) + + // The headers getter steps are to return this’s headers. + return this[kHeaders] + } + + get body () { + webidl.brandCheck(this, Response) + + return this[kState].body ? this[kState].body.stream : null + } + + get bodyUsed () { + webidl.brandCheck(this, Response) + + return !!this[kState].body && util.isDisturbed(this[kState].body.stream) + } + + // Returns a clone of response. + clone () { + webidl.brandCheck(this, Response) + + // 1. If this is unusable, then throw a TypeError. + if (this.bodyUsed || (this.body && this.body.locked)) { + throw webidl.errors.exception({ + header: 'Response.clone', + message: 'Body has already been consumed.' + }) + } + + // 2. Let clonedResponse be the result of cloning this’s response. + const clonedResponse = cloneResponse(this[kState]) + + // 3. Return the result of creating a Response object, given + // clonedResponse, this’s headers’s guard, and this’s relevant Realm. + const clonedResponseObject = new Response() + clonedResponseObject[kState] = clonedResponse + clonedResponseObject[kRealm] = this[kRealm] + clonedResponseObject[kHeaders][kHeadersList] = clonedResponse.headersList + clonedResponseObject[kHeaders][kGuard] = this[kHeaders][kGuard] + clonedResponseObject[kHeaders][kRealm] = this[kHeaders][kRealm] + + return clonedResponseObject + } +} + +mixinBody(Response) + +Object.defineProperties(Response.prototype, { + type: kEnumerableProperty, + url: kEnumerableProperty, + status: kEnumerableProperty, + ok: kEnumerableProperty, + redirected: kEnumerableProperty, + statusText: kEnumerableProperty, + headers: kEnumerableProperty, + clone: kEnumerableProperty, + body: kEnumerableProperty, + bodyUsed: kEnumerableProperty, + [Symbol.toStringTag]: { + value: 'Response', + configurable: true + } +}) + +Object.defineProperties(Response, { + json: kEnumerableProperty, + redirect: kEnumerableProperty, + error: kEnumerableProperty +}) + +// https://fetch.spec.whatwg.org/#concept-response-clone +function cloneResponse (response) { + // To clone a response response, run these steps: + + // 1. If response is a filtered response, then return a new identical + // filtered response whose internal response is a clone of response’s + // internal response. + if (response.internalResponse) { + return filterResponse( + cloneResponse(response.internalResponse), + response.type + ) + } + + // 2. Let newResponse be a copy of response, except for its body. + const newResponse = makeResponse({ ...response, body: null }) + + // 3. If response’s body is non-null, then set newResponse’s body to the + // result of cloning response’s body. + if (response.body != null) { + newResponse.body = cloneBody(response.body) + } + + // 4. Return newResponse. + return newResponse +} + +function makeResponse (init) { + return { + aborted: false, + rangeRequested: false, + timingAllowPassed: false, + requestIncludesCredentials: false, + type: 'default', + status: 200, + timingInfo: null, + cacheState: '', + statusText: '', + ...init, + headersList: init.headersList + ? new HeadersList(init.headersList) + : new HeadersList(), + urlList: init.urlList ? [...init.urlList] : [] + } +} + +function makeNetworkError (reason) { + const isError = isErrorLike(reason) + return makeResponse({ + type: 'error', + status: 0, + error: isError + ? reason + : new Error(reason ? String(reason) : reason), + aborted: reason && reason.name === 'AbortError' + }) +} + +function makeFilteredResponse (response, state) { + state = { + internalResponse: response, + ...state + } + + return new Proxy(response, { + get (target, p) { + return p in state ? state[p] : target[p] + }, + set (target, p, value) { + assert(!(p in state)) + target[p] = value + return true + } + }) +} + +// https://fetch.spec.whatwg.org/#concept-filtered-response +function filterResponse (response, type) { + // Set response to the following filtered response with response as its + // internal response, depending on request’s response tainting: + if (type === 'basic') { + // A basic filtered response is a filtered response whose type is "basic" + // and header list excludes any headers in internal response’s header list + // whose name is a forbidden response-header name. + + // Note: undici does not implement forbidden response-header names + return makeFilteredResponse(response, { + type: 'basic', + headersList: response.headersList + }) + } else if (type === 'cors') { + // A CORS filtered response is a filtered response whose type is "cors" + // and header list excludes any headers in internal response’s header + // list whose name is not a CORS-safelisted response-header name, given + // internal response’s CORS-exposed header-name list. + + // Note: undici does not implement CORS-safelisted response-header names + return makeFilteredResponse(response, { + type: 'cors', + headersList: response.headersList + }) + } else if (type === 'opaque') { + // An opaque filtered response is a filtered response whose type is + // "opaque", URL list is the empty list, status is 0, status message + // is the empty byte sequence, header list is empty, and body is null. + + return makeFilteredResponse(response, { + type: 'opaque', + urlList: Object.freeze([]), + status: 0, + statusText: '', + body: null + }) + } else if (type === 'opaqueredirect') { + // An opaque-redirect filtered response is a filtered response whose type + // is "opaqueredirect", status is 0, status message is the empty byte + // sequence, header list is empty, and body is null. + + return makeFilteredResponse(response, { + type: 'opaqueredirect', + status: 0, + statusText: '', + headersList: [], + body: null + }) + } else { + assert(false) + } +} + +// https://fetch.spec.whatwg.org/#appropriate-network-error +function makeAppropriateNetworkError (fetchParams, err = null) { + // 1. Assert: fetchParams is canceled. + assert(isCancelled(fetchParams)) + + // 2. Return an aborted network error if fetchParams is aborted; + // otherwise return a network error. + return isAborted(fetchParams) + ? makeNetworkError(Object.assign(new DOMException('The operation was aborted.', 'AbortError'), { cause: err })) + : makeNetworkError(Object.assign(new DOMException('Request was cancelled.'), { cause: err })) +} + +// https://whatpr.org/fetch/1392.html#initialize-a-response +function initializeResponse (response, init, body) { + // 1. If init["status"] is not in the range 200 to 599, inclusive, then + // throw a RangeError. + if (init.status !== null && (init.status < 200 || init.status > 599)) { + throw new RangeError('init["status"] must be in the range of 200 to 599, inclusive.') + } + + // 2. If init["statusText"] does not match the reason-phrase token production, + // then throw a TypeError. + if ('statusText' in init && init.statusText != null) { + // See, https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2: + // reason-phrase = *( HTAB / SP / VCHAR / obs-text ) + if (!isValidReasonPhrase(String(init.statusText))) { + throw new TypeError('Invalid statusText') + } + } + + // 3. Set response’s response’s status to init["status"]. + if ('status' in init && init.status != null) { + response[kState].status = init.status + } + + // 4. Set response’s response’s status message to init["statusText"]. + if ('statusText' in init && init.statusText != null) { + response[kState].statusText = init.statusText + } + + // 5. If init["headers"] exists, then fill response’s headers with init["headers"]. + if ('headers' in init && init.headers != null) { + fill(response[kHeaders], init.headers) + } + + // 6. If body was given, then: + if (body) { + // 1. If response's status is a null body status, then throw a TypeError. + if (nullBodyStatus.includes(response.status)) { + throw webidl.errors.exception({ + header: 'Response constructor', + message: 'Invalid response status code ' + response.status + }) + } + + // 2. Set response's body to body's body. + response[kState].body = body.body + + // 3. If body's type is non-null and response's header list does not contain + // `Content-Type`, then append (`Content-Type`, body's type) to response's header list. + if (body.type != null && !response[kState].headersList.contains('Content-Type')) { + response[kState].headersList.append('content-type', body.type) + } + } +} + +webidl.converters.ReadableStream = webidl.interfaceConverter( + ReadableStream +) + +webidl.converters.FormData = webidl.interfaceConverter( + FormData +) + +webidl.converters.URLSearchParams = webidl.interfaceConverter( + URLSearchParams +) + +// https://fetch.spec.whatwg.org/#typedefdef-xmlhttprequestbodyinit +webidl.converters.XMLHttpRequestBodyInit = function (V) { + if (typeof V === 'string') { + return webidl.converters.USVString(V) + } + + if (isBlobLike(V)) { + return webidl.converters.Blob(V, { strict: false }) + } + + if ( + types.isAnyArrayBuffer(V) || + types.isTypedArray(V) || + types.isDataView(V) + ) { + return webidl.converters.BufferSource(V) + } + + if (util.isFormDataLike(V)) { + return webidl.converters.FormData(V, { strict: false }) + } + + if (V instanceof URLSearchParams) { + return webidl.converters.URLSearchParams(V) + } + + return webidl.converters.DOMString(V) +} + +// https://fetch.spec.whatwg.org/#bodyinit +webidl.converters.BodyInit = function (V) { + if (V instanceof ReadableStream) { + return webidl.converters.ReadableStream(V) + } + + // Note: the spec doesn't include async iterables, + // this is an undici extension. + if (V?.[Symbol.asyncIterator]) { + return V + } + + return webidl.converters.XMLHttpRequestBodyInit(V) +} + +webidl.converters.ResponseInit = webidl.dictionaryConverter([ + { + key: 'status', + converter: webidl.converters['unsigned short'], + defaultValue: 200 + }, + { + key: 'statusText', + converter: webidl.converters.ByteString, + defaultValue: '' + }, + { + key: 'headers', + converter: webidl.converters.HeadersInit + } +]) + +module.exports = { + makeNetworkError, + makeResponse, + makeAppropriateNetworkError, + filterResponse, + Response, + cloneResponse +} + + +/***/ }), + +/***/ 5861: +/***/ ((module) => { + +"use strict"; + + +module.exports = { + kUrl: Symbol('url'), + kHeaders: Symbol('headers'), + kSignal: Symbol('signal'), + kState: Symbol('state'), + kGuard: Symbol('guard'), + kRealm: Symbol('realm') +} + + +/***/ }), + +/***/ 2538: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { redirectStatusSet, referrerPolicySet: referrerPolicyTokens, badPortsSet } = __nccwpck_require__(1037) +const { getGlobalOrigin } = __nccwpck_require__(1246) +const { performance } = __nccwpck_require__(4074) +const { isBlobLike, toUSVString, ReadableStreamFrom } = __nccwpck_require__(3983) +const assert = __nccwpck_require__(9491) +const { isUint8Array } = __nccwpck_require__(9830) + +// https://nodejs.org/api/crypto.html#determining-if-crypto-support-is-unavailable +/** @type {import('crypto')|undefined} */ +let crypto + +try { + crypto = __nccwpck_require__(6113) +} catch { + +} + +function responseURL (response) { + // https://fetch.spec.whatwg.org/#responses + // A response has an associated URL. It is a pointer to the last URL + // in response’s URL list and null if response’s URL list is empty. + const urlList = response.urlList + const length = urlList.length + return length === 0 ? null : urlList[length - 1].toString() +} + +// https://fetch.spec.whatwg.org/#concept-response-location-url +function responseLocationURL (response, requestFragment) { + // 1. If response’s status is not a redirect status, then return null. + if (!redirectStatusSet.has(response.status)) { + return null + } + + // 2. Let location be the result of extracting header list values given + // `Location` and response’s header list. + let location = response.headersList.get('location') + + // 3. If location is a header value, then set location to the result of + // parsing location with response’s URL. + if (location !== null && isValidHeaderValue(location)) { + location = new URL(location, responseURL(response)) + } + + // 4. If location is a URL whose fragment is null, then set location’s + // fragment to requestFragment. + if (location && !location.hash) { + location.hash = requestFragment + } + + // 5. Return location. + return location +} + +/** @returns {URL} */ +function requestCurrentURL (request) { + return request.urlList[request.urlList.length - 1] +} + +function requestBadPort (request) { + // 1. Let url be request’s current URL. + const url = requestCurrentURL(request) + + // 2. If url’s scheme is an HTTP(S) scheme and url’s port is a bad port, + // then return blocked. + if (urlIsHttpHttpsScheme(url) && badPortsSet.has(url.port)) { + return 'blocked' + } + + // 3. Return allowed. + return 'allowed' +} + +function isErrorLike (object) { + return object instanceof Error || ( + object?.constructor?.name === 'Error' || + object?.constructor?.name === 'DOMException' + ) +} + +// Check whether |statusText| is a ByteString and +// matches the Reason-Phrase token production. +// RFC 2616: https://tools.ietf.org/html/rfc2616 +// RFC 7230: https://tools.ietf.org/html/rfc7230 +// "reason-phrase = *( HTAB / SP / VCHAR / obs-text )" +// https://github.com/chromium/chromium/blob/94.0.4604.1/third_party/blink/renderer/core/fetch/response.cc#L116 +function isValidReasonPhrase (statusText) { + for (let i = 0; i < statusText.length; ++i) { + const c = statusText.charCodeAt(i) + if ( + !( + ( + c === 0x09 || // HTAB + (c >= 0x20 && c <= 0x7e) || // SP / VCHAR + (c >= 0x80 && c <= 0xff) + ) // obs-text + ) + ) { + return false + } + } + return true +} + +function isTokenChar (c) { + return !( + c >= 0x7f || + c <= 0x20 || + c === '(' || + c === ')' || + c === '<' || + c === '>' || + c === '@' || + c === ',' || + c === ';' || + c === ':' || + c === '\\' || + c === '"' || + c === '/' || + c === '[' || + c === ']' || + c === '?' || + c === '=' || + c === '{' || + c === '}' + ) +} + +// See RFC 7230, Section 3.2.6. +// https://github.com/chromium/chromium/blob/d7da0240cae77824d1eda25745c4022757499131/third_party/blink/renderer/platform/network/http_parsers.cc#L321 +function isValidHTTPToken (characters) { + if (!characters || typeof characters !== 'string') { + return false + } + for (let i = 0; i < characters.length; ++i) { + const c = characters.charCodeAt(i) + if (c > 0x7f || !isTokenChar(c)) { + return false + } + } + return true +} + +// https://fetch.spec.whatwg.org/#header-name +// https://github.com/chromium/chromium/blob/b3d37e6f94f87d59e44662d6078f6a12de845d17/net/http/http_util.cc#L342 +function isValidHeaderName (potentialValue) { + if (potentialValue.length === 0) { + return false + } + + return isValidHTTPToken(potentialValue) +} + +/** + * @see https://fetch.spec.whatwg.org/#header-value + * @param {string} potentialValue + */ +function isValidHeaderValue (potentialValue) { + // - Has no leading or trailing HTTP tab or space bytes. + // - Contains no 0x00 (NUL) or HTTP newline bytes. + if ( + potentialValue.startsWith('\t') || + potentialValue.startsWith(' ') || + potentialValue.endsWith('\t') || + potentialValue.endsWith(' ') + ) { + return false + } + + if ( + potentialValue.includes('\0') || + potentialValue.includes('\r') || + potentialValue.includes('\n') + ) { + return false + } + + return true +} + +// https://w3c.github.io/webappsec-referrer-policy/#set-requests-referrer-policy-on-redirect +function setRequestReferrerPolicyOnRedirect (request, actualResponse) { + // Given a request request and a response actualResponse, this algorithm + // updates request’s referrer policy according to the Referrer-Policy + // header (if any) in actualResponse. + + // 1. Let policy be the result of executing § 8.1 Parse a referrer policy + // from a Referrer-Policy header on actualResponse. + + // 8.1 Parse a referrer policy from a Referrer-Policy header + // 1. Let policy-tokens be the result of extracting header list values given `Referrer-Policy` and response’s header list. + const { headersList } = actualResponse + // 2. Let policy be the empty string. + // 3. For each token in policy-tokens, if token is a referrer policy and token is not the empty string, then set policy to token. + // 4. Return policy. + const policyHeader = (headersList.get('referrer-policy') ?? '').split(',') + + // Note: As the referrer-policy can contain multiple policies + // separated by comma, we need to loop through all of them + // and pick the first valid one. + // Ref: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy#specify_a_fallback_policy + let policy = '' + if (policyHeader.length > 0) { + // The right-most policy takes precedence. + // The left-most policy is the fallback. + for (let i = policyHeader.length; i !== 0; i--) { + const token = policyHeader[i - 1].trim() + if (referrerPolicyTokens.has(token)) { + policy = token + break + } + } + } + + // 2. If policy is not the empty string, then set request’s referrer policy to policy. + if (policy !== '') { + request.referrerPolicy = policy + } +} + +// https://fetch.spec.whatwg.org/#cross-origin-resource-policy-check +function crossOriginResourcePolicyCheck () { + // TODO + return 'allowed' +} + +// https://fetch.spec.whatwg.org/#concept-cors-check +function corsCheck () { + // TODO + return 'success' +} + +// https://fetch.spec.whatwg.org/#concept-tao-check +function TAOCheck () { + // TODO + return 'success' +} + +function appendFetchMetadata (httpRequest) { + // https://w3c.github.io/webappsec-fetch-metadata/#sec-fetch-dest-header + // TODO + + // https://w3c.github.io/webappsec-fetch-metadata/#sec-fetch-mode-header + + // 1. Assert: r’s url is a potentially trustworthy URL. + // TODO + + // 2. Let header be a Structured Header whose value is a token. + let header = null + + // 3. Set header’s value to r’s mode. + header = httpRequest.mode + + // 4. Set a structured field value `Sec-Fetch-Mode`/header in r’s header list. + httpRequest.headersList.set('sec-fetch-mode', header) + + // https://w3c.github.io/webappsec-fetch-metadata/#sec-fetch-site-header + // TODO + + // https://w3c.github.io/webappsec-fetch-metadata/#sec-fetch-user-header + // TODO +} + +// https://fetch.spec.whatwg.org/#append-a-request-origin-header +function appendRequestOriginHeader (request) { + // 1. Let serializedOrigin be the result of byte-serializing a request origin with request. + let serializedOrigin = request.origin + + // 2. If request’s response tainting is "cors" or request’s mode is "websocket", then append (`Origin`, serializedOrigin) to request’s header list. + if (request.responseTainting === 'cors' || request.mode === 'websocket') { + if (serializedOrigin) { + request.headersList.append('origin', serializedOrigin) + } + + // 3. Otherwise, if request’s method is neither `GET` nor `HEAD`, then: + } else if (request.method !== 'GET' && request.method !== 'HEAD') { + // 1. Switch on request’s referrer policy: + switch (request.referrerPolicy) { + case 'no-referrer': + // Set serializedOrigin to `null`. + serializedOrigin = null + break + case 'no-referrer-when-downgrade': + case 'strict-origin': + case 'strict-origin-when-cross-origin': + // If request’s origin is a tuple origin, its scheme is "https", and request’s current URL’s scheme is not "https", then set serializedOrigin to `null`. + if (request.origin && urlHasHttpsScheme(request.origin) && !urlHasHttpsScheme(requestCurrentURL(request))) { + serializedOrigin = null + } + break + case 'same-origin': + // If request’s origin is not same origin with request’s current URL’s origin, then set serializedOrigin to `null`. + if (!sameOrigin(request, requestCurrentURL(request))) { + serializedOrigin = null + } + break + default: + // Do nothing. + } + + if (serializedOrigin) { + // 2. Append (`Origin`, serializedOrigin) to request’s header list. + request.headersList.append('origin', serializedOrigin) + } + } +} + +function coarsenedSharedCurrentTime (crossOriginIsolatedCapability) { + // TODO + return performance.now() +} + +// https://fetch.spec.whatwg.org/#create-an-opaque-timing-info +function createOpaqueTimingInfo (timingInfo) { + return { + startTime: timingInfo.startTime ?? 0, + redirectStartTime: 0, + redirectEndTime: 0, + postRedirectStartTime: timingInfo.startTime ?? 0, + finalServiceWorkerStartTime: 0, + finalNetworkResponseStartTime: 0, + finalNetworkRequestStartTime: 0, + endTime: 0, + encodedBodySize: 0, + decodedBodySize: 0, + finalConnectionTimingInfo: null + } +} + +// https://html.spec.whatwg.org/multipage/origin.html#policy-container +function makePolicyContainer () { + // Note: the fetch spec doesn't make use of embedder policy or CSP list + return { + referrerPolicy: 'strict-origin-when-cross-origin' + } +} + +// https://html.spec.whatwg.org/multipage/origin.html#clone-a-policy-container +function clonePolicyContainer (policyContainer) { + return { + referrerPolicy: policyContainer.referrerPolicy + } +} + +// https://w3c.github.io/webappsec-referrer-policy/#determine-requests-referrer +function determineRequestsReferrer (request) { + // 1. Let policy be request's referrer policy. + const policy = request.referrerPolicy + + // Note: policy cannot (shouldn't) be null or an empty string. + assert(policy) + + // 2. Let environment be request’s client. + + let referrerSource = null + + // 3. Switch on request’s referrer: + if (request.referrer === 'client') { + // Note: node isn't a browser and doesn't implement document/iframes, + // so we bypass this step and replace it with our own. + + const globalOrigin = getGlobalOrigin() + + if (!globalOrigin || globalOrigin.origin === 'null') { + return 'no-referrer' + } + + // note: we need to clone it as it's mutated + referrerSource = new URL(globalOrigin) + } else if (request.referrer instanceof URL) { + // Let referrerSource be request’s referrer. + referrerSource = request.referrer + } + + // 4. Let request’s referrerURL be the result of stripping referrerSource for + // use as a referrer. + let referrerURL = stripURLForReferrer(referrerSource) + + // 5. Let referrerOrigin be the result of stripping referrerSource for use as + // a referrer, with the origin-only flag set to true. + const referrerOrigin = stripURLForReferrer(referrerSource, true) + + // 6. If the result of serializing referrerURL is a string whose length is + // greater than 4096, set referrerURL to referrerOrigin. + if (referrerURL.toString().length > 4096) { + referrerURL = referrerOrigin + } + + const areSameOrigin = sameOrigin(request, referrerURL) + const isNonPotentiallyTrustWorthy = isURLPotentiallyTrustworthy(referrerURL) && + !isURLPotentiallyTrustworthy(request.url) + + // 8. Execute the switch statements corresponding to the value of policy: + switch (policy) { + case 'origin': return referrerOrigin != null ? referrerOrigin : stripURLForReferrer(referrerSource, true) + case 'unsafe-url': return referrerURL + case 'same-origin': + return areSameOrigin ? referrerOrigin : 'no-referrer' + case 'origin-when-cross-origin': + return areSameOrigin ? referrerURL : referrerOrigin + case 'strict-origin-when-cross-origin': { + const currentURL = requestCurrentURL(request) + + // 1. If the origin of referrerURL and the origin of request’s current + // URL are the same, then return referrerURL. + if (sameOrigin(referrerURL, currentURL)) { + return referrerURL + } + + // 2. If referrerURL is a potentially trustworthy URL and request’s + // current URL is not a potentially trustworthy URL, then return no + // referrer. + if (isURLPotentiallyTrustworthy(referrerURL) && !isURLPotentiallyTrustworthy(currentURL)) { + return 'no-referrer' + } + + // 3. Return referrerOrigin. + return referrerOrigin + } + case 'strict-origin': // eslint-disable-line + /** + * 1. If referrerURL is a potentially trustworthy URL and + * request’s current URL is not a potentially trustworthy URL, + * then return no referrer. + * 2. Return referrerOrigin + */ + case 'no-referrer-when-downgrade': // eslint-disable-line + /** + * 1. If referrerURL is a potentially trustworthy URL and + * request’s current URL is not a potentially trustworthy URL, + * then return no referrer. + * 2. Return referrerOrigin + */ + + default: // eslint-disable-line + return isNonPotentiallyTrustWorthy ? 'no-referrer' : referrerOrigin + } +} + +/** + * @see https://w3c.github.io/webappsec-referrer-policy/#strip-url + * @param {URL} url + * @param {boolean|undefined} originOnly + */ +function stripURLForReferrer (url, originOnly) { + // 1. Assert: url is a URL. + assert(url instanceof URL) + + // 2. If url’s scheme is a local scheme, then return no referrer. + if (url.protocol === 'file:' || url.protocol === 'about:' || url.protocol === 'blank:') { + return 'no-referrer' + } + + // 3. Set url’s username to the empty string. + url.username = '' + + // 4. Set url’s password to the empty string. + url.password = '' + + // 5. Set url’s fragment to null. + url.hash = '' + + // 6. If the origin-only flag is true, then: + if (originOnly) { + // 1. Set url’s path to « the empty string ». + url.pathname = '' + + // 2. Set url’s query to null. + url.search = '' + } + + // 7. Return url. + return url +} + +function isURLPotentiallyTrustworthy (url) { + if (!(url instanceof URL)) { + return false + } + + // If child of about, return true + if (url.href === 'about:blank' || url.href === 'about:srcdoc') { + return true + } + + // If scheme is data, return true + if (url.protocol === 'data:') return true + + // If file, return true + if (url.protocol === 'file:') return true + + return isOriginPotentiallyTrustworthy(url.origin) + + function isOriginPotentiallyTrustworthy (origin) { + // If origin is explicitly null, return false + if (origin == null || origin === 'null') return false + + const originAsURL = new URL(origin) + + // If secure, return true + if (originAsURL.protocol === 'https:' || originAsURL.protocol === 'wss:') { + return true + } + + // If localhost or variants, return true + if (/^127(?:\.[0-9]+){0,2}\.[0-9]+$|^\[(?:0*:)*?:?0*1\]$/.test(originAsURL.hostname) || + (originAsURL.hostname === 'localhost' || originAsURL.hostname.includes('localhost.')) || + (originAsURL.hostname.endsWith('.localhost'))) { + return true + } + + // If any other, return false + return false + } +} + +/** + * @see https://w3c.github.io/webappsec-subresource-integrity/#does-response-match-metadatalist + * @param {Uint8Array} bytes + * @param {string} metadataList + */ +function bytesMatch (bytes, metadataList) { + // If node is not built with OpenSSL support, we cannot check + // a request's integrity, so allow it by default (the spec will + // allow requests if an invalid hash is given, as precedence). + /* istanbul ignore if: only if node is built with --without-ssl */ + if (crypto === undefined) { + return true + } + + // 1. Let parsedMetadata be the result of parsing metadataList. + const parsedMetadata = parseMetadata(metadataList) + + // 2. If parsedMetadata is no metadata, return true. + if (parsedMetadata === 'no metadata') { + return true + } + + // 3. If parsedMetadata is the empty set, return true. + if (parsedMetadata.length === 0) { + return true + } + + // 4. Let metadata be the result of getting the strongest + // metadata from parsedMetadata. + const list = parsedMetadata.sort((c, d) => d.algo.localeCompare(c.algo)) + // get the strongest algorithm + const strongest = list[0].algo + // get all entries that use the strongest algorithm; ignore weaker + const metadata = list.filter((item) => item.algo === strongest) + + // 5. For each item in metadata: + for (const item of metadata) { + // 1. Let algorithm be the alg component of item. + const algorithm = item.algo + + // 2. Let expectedValue be the val component of item. + let expectedValue = item.hash + + // See https://github.com/web-platform-tests/wpt/commit/e4c5cc7a5e48093220528dfdd1c4012dc3837a0e + // "be liberal with padding". This is annoying, and it's not even in the spec. + + if (expectedValue.endsWith('==')) { + expectedValue = expectedValue.slice(0, -2) + } + + // 3. Let actualValue be the result of applying algorithm to bytes. + let actualValue = crypto.createHash(algorithm).update(bytes).digest('base64') + + if (actualValue.endsWith('==')) { + actualValue = actualValue.slice(0, -2) + } + + // 4. If actualValue is a case-sensitive match for expectedValue, + // return true. + if (actualValue === expectedValue) { + return true + } + + let actualBase64URL = crypto.createHash(algorithm).update(bytes).digest('base64url') + + if (actualBase64URL.endsWith('==')) { + actualBase64URL = actualBase64URL.slice(0, -2) + } + + if (actualBase64URL === expectedValue) { + return true + } + } + + // 6. Return false. + return false +} + +// https://w3c.github.io/webappsec-subresource-integrity/#grammardef-hash-with-options +// https://www.w3.org/TR/CSP2/#source-list-syntax +// https://www.rfc-editor.org/rfc/rfc5234#appendix-B.1 +const parseHashWithOptions = /((?sha256|sha384|sha512)-(?[A-z0-9+/]{1}.*={0,2}))( +[\x21-\x7e]?)?/i + +/** + * @see https://w3c.github.io/webappsec-subresource-integrity/#parse-metadata + * @param {string} metadata + */ +function parseMetadata (metadata) { + // 1. Let result be the empty set. + /** @type {{ algo: string, hash: string }[]} */ + const result = [] + + // 2. Let empty be equal to true. + let empty = true + + const supportedHashes = crypto.getHashes() + + // 3. For each token returned by splitting metadata on spaces: + for (const token of metadata.split(' ')) { + // 1. Set empty to false. + empty = false + + // 2. Parse token as a hash-with-options. + const parsedToken = parseHashWithOptions.exec(token) + + // 3. If token does not parse, continue to the next token. + if (parsedToken === null || parsedToken.groups === undefined) { + // Note: Chromium blocks the request at this point, but Firefox + // gives a warning that an invalid integrity was given. The + // correct behavior is to ignore these, and subsequently not + // check the integrity of the resource. + continue + } + + // 4. Let algorithm be the hash-algo component of token. + const algorithm = parsedToken.groups.algo + + // 5. If algorithm is a hash function recognized by the user + // agent, add the parsed token to result. + if (supportedHashes.includes(algorithm.toLowerCase())) { + result.push(parsedToken.groups) + } + } + + // 4. Return no metadata if empty is true, otherwise return result. + if (empty === true) { + return 'no metadata' + } + + return result +} + +// https://w3c.github.io/webappsec-upgrade-insecure-requests/#upgrade-request +function tryUpgradeRequestToAPotentiallyTrustworthyURL (request) { + // TODO +} + +/** + * @link {https://html.spec.whatwg.org/multipage/origin.html#same-origin} + * @param {URL} A + * @param {URL} B + */ +function sameOrigin (A, B) { + // 1. If A and B are the same opaque origin, then return true. + if (A.origin === B.origin && A.origin === 'null') { + return true + } + + // 2. If A and B are both tuple origins and their schemes, + // hosts, and port are identical, then return true. + if (A.protocol === B.protocol && A.hostname === B.hostname && A.port === B.port) { + return true + } + + // 3. Return false. + return false +} + +function createDeferredPromise () { + let res + let rej + const promise = new Promise((resolve, reject) => { + res = resolve + rej = reject + }) + + return { promise, resolve: res, reject: rej } +} + +function isAborted (fetchParams) { + return fetchParams.controller.state === 'aborted' +} + +function isCancelled (fetchParams) { + return fetchParams.controller.state === 'aborted' || + fetchParams.controller.state === 'terminated' +} + +// https://fetch.spec.whatwg.org/#concept-method-normalize +function normalizeMethod (method) { + return /^(DELETE|GET|HEAD|OPTIONS|POST|PUT)$/i.test(method) + ? method.toUpperCase() + : method +} + +// https://infra.spec.whatwg.org/#serialize-a-javascript-value-to-a-json-string +function serializeJavascriptValueToJSONString (value) { + // 1. Let result be ? Call(%JSON.stringify%, undefined, « value »). + const result = JSON.stringify(value) + + // 2. If result is undefined, then throw a TypeError. + if (result === undefined) { + throw new TypeError('Value is not JSON serializable') + } + + // 3. Assert: result is a string. + assert(typeof result === 'string') + + // 4. Return result. + return result +} + +// https://tc39.es/ecma262/#sec-%25iteratorprototype%25-object +const esIteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]())) + +/** + * @see https://webidl.spec.whatwg.org/#dfn-iterator-prototype-object + * @param {() => unknown[]} iterator + * @param {string} name name of the instance + * @param {'key'|'value'|'key+value'} kind + */ +function makeIterator (iterator, name, kind) { + const object = { + index: 0, + kind, + target: iterator + } + + const i = { + next () { + // 1. Let interface be the interface for which the iterator prototype object exists. + + // 2. Let thisValue be the this value. + + // 3. Let object be ? ToObject(thisValue). + + // 4. If object is a platform object, then perform a security + // check, passing: + + // 5. If object is not a default iterator object for interface, + // then throw a TypeError. + if (Object.getPrototypeOf(this) !== i) { + throw new TypeError( + `'next' called on an object that does not implement interface ${name} Iterator.` + ) + } + + // 6. Let index be object’s index. + // 7. Let kind be object’s kind. + // 8. Let values be object’s target's value pairs to iterate over. + const { index, kind, target } = object + const values = target() + + // 9. Let len be the length of values. + const len = values.length + + // 10. If index is greater than or equal to len, then return + // CreateIterResultObject(undefined, true). + if (index >= len) { + return { value: undefined, done: true } + } + + // 11. Let pair be the entry in values at index index. + const pair = values[index] + + // 12. Set object’s index to index + 1. + object.index = index + 1 + + // 13. Return the iterator result for pair and kind. + return iteratorResult(pair, kind) + }, + // The class string of an iterator prototype object for a given interface is the + // result of concatenating the identifier of the interface and the string " Iterator". + [Symbol.toStringTag]: `${name} Iterator` + } + + // The [[Prototype]] internal slot of an iterator prototype object must be %IteratorPrototype%. + Object.setPrototypeOf(i, esIteratorPrototype) + // esIteratorPrototype needs to be the prototype of i + // which is the prototype of an empty object. Yes, it's confusing. + return Object.setPrototypeOf({}, i) +} + +// https://webidl.spec.whatwg.org/#iterator-result +function iteratorResult (pair, kind) { + let result + + // 1. Let result be a value determined by the value of kind: + switch (kind) { + case 'key': { + // 1. Let idlKey be pair’s key. + // 2. Let key be the result of converting idlKey to an + // ECMAScript value. + // 3. result is key. + result = pair[0] + break + } + case 'value': { + // 1. Let idlValue be pair’s value. + // 2. Let value be the result of converting idlValue to + // an ECMAScript value. + // 3. result is value. + result = pair[1] + break + } + case 'key+value': { + // 1. Let idlKey be pair’s key. + // 2. Let idlValue be pair’s value. + // 3. Let key be the result of converting idlKey to an + // ECMAScript value. + // 4. Let value be the result of converting idlValue to + // an ECMAScript value. + // 5. Let array be ! ArrayCreate(2). + // 6. Call ! CreateDataProperty(array, "0", key). + // 7. Call ! CreateDataProperty(array, "1", value). + // 8. result is array. + result = pair + break + } + } + + // 2. Return CreateIterResultObject(result, false). + return { value: result, done: false } +} + +/** + * @see https://fetch.spec.whatwg.org/#body-fully-read + */ +async function fullyReadBody (body, processBody, processBodyError) { + // 1. If taskDestination is null, then set taskDestination to + // the result of starting a new parallel queue. + + // 2. Let successSteps given a byte sequence bytes be to queue a + // fetch task to run processBody given bytes, with taskDestination. + const successSteps = processBody + + // 3. Let errorSteps be to queue a fetch task to run processBodyError, + // with taskDestination. + const errorSteps = processBodyError + + // 4. Let reader be the result of getting a reader for body’s stream. + // If that threw an exception, then run errorSteps with that + // exception and return. + let reader + + try { + reader = body.stream.getReader() + } catch (e) { + errorSteps(e) + return + } + + // 5. Read all bytes from reader, given successSteps and errorSteps. + try { + const result = await readAllBytes(reader) + successSteps(result) + } catch (e) { + errorSteps(e) + } +} + +/** @type {ReadableStream} */ +let ReadableStream = globalThis.ReadableStream + +function isReadableStreamLike (stream) { + if (!ReadableStream) { + ReadableStream = (__nccwpck_require__(5356).ReadableStream) + } + + return stream instanceof ReadableStream || ( + stream[Symbol.toStringTag] === 'ReadableStream' && + typeof stream.tee === 'function' + ) +} + +const MAXIMUM_ARGUMENT_LENGTH = 65535 + +/** + * @see https://infra.spec.whatwg.org/#isomorphic-decode + * @param {number[]|Uint8Array} input + */ +function isomorphicDecode (input) { + // 1. To isomorphic decode a byte sequence input, return a string whose code point + // length is equal to input’s length and whose code points have the same values + // as the values of input’s bytes, in the same order. + + if (input.length < MAXIMUM_ARGUMENT_LENGTH) { + return String.fromCharCode(...input) + } + + return input.reduce((previous, current) => previous + String.fromCharCode(current), '') +} + +/** + * @param {ReadableStreamController} controller + */ +function readableStreamClose (controller) { + try { + controller.close() + } catch (err) { + // TODO: add comment explaining why this error occurs. + if (!err.message.includes('Controller is already closed')) { + throw err + } + } +} + +/** + * @see https://infra.spec.whatwg.org/#isomorphic-encode + * @param {string} input + */ +function isomorphicEncode (input) { + // 1. Assert: input contains no code points greater than U+00FF. + for (let i = 0; i < input.length; i++) { + assert(input.charCodeAt(i) <= 0xFF) + } + + // 2. Return a byte sequence whose length is equal to input’s code + // point length and whose bytes have the same values as the + // values of input’s code points, in the same order + return input +} + +/** + * @see https://streams.spec.whatwg.org/#readablestreamdefaultreader-read-all-bytes + * @see https://streams.spec.whatwg.org/#read-loop + * @param {ReadableStreamDefaultReader} reader + */ +async function readAllBytes (reader) { + const bytes = [] + let byteLength = 0 + + while (true) { + const { done, value: chunk } = await reader.read() + + if (done) { + // 1. Call successSteps with bytes. + return Buffer.concat(bytes, byteLength) + } + + // 1. If chunk is not a Uint8Array object, call failureSteps + // with a TypeError and abort these steps. + if (!isUint8Array(chunk)) { + throw new TypeError('Received non-Uint8Array chunk') + } + + // 2. Append the bytes represented by chunk to bytes. + bytes.push(chunk) + byteLength += chunk.length + + // 3. Read-loop given reader, bytes, successSteps, and failureSteps. + } +} + +/** + * @see https://fetch.spec.whatwg.org/#is-local + * @param {URL} url + */ +function urlIsLocal (url) { + assert('protocol' in url) // ensure it's a url object + + const protocol = url.protocol + + return protocol === 'about:' || protocol === 'blob:' || protocol === 'data:' +} + +/** + * @param {string|URL} url + */ +function urlHasHttpsScheme (url) { + if (typeof url === 'string') { + return url.startsWith('https:') + } + + return url.protocol === 'https:' +} + +/** + * @see https://fetch.spec.whatwg.org/#http-scheme + * @param {URL} url + */ +function urlIsHttpHttpsScheme (url) { + assert('protocol' in url) // ensure it's a url object + + const protocol = url.protocol + + return protocol === 'http:' || protocol === 'https:' +} + +/** + * Fetch supports node >= 16.8.0, but Object.hasOwn was added in v16.9.0. + */ +const hasOwn = Object.hasOwn || ((dict, key) => Object.prototype.hasOwnProperty.call(dict, key)) + +module.exports = { + isAborted, + isCancelled, + createDeferredPromise, + ReadableStreamFrom, + toUSVString, + tryUpgradeRequestToAPotentiallyTrustworthyURL, + coarsenedSharedCurrentTime, + determineRequestsReferrer, + makePolicyContainer, + clonePolicyContainer, + appendFetchMetadata, + appendRequestOriginHeader, + TAOCheck, + corsCheck, + crossOriginResourcePolicyCheck, + createOpaqueTimingInfo, + setRequestReferrerPolicyOnRedirect, + isValidHTTPToken, + requestBadPort, + requestCurrentURL, + responseURL, + responseLocationURL, + isBlobLike, + isURLPotentiallyTrustworthy, + isValidReasonPhrase, + sameOrigin, + normalizeMethod, + serializeJavascriptValueToJSONString, + makeIterator, + isValidHeaderName, + isValidHeaderValue, + hasOwn, + isErrorLike, + fullyReadBody, + bytesMatch, + isReadableStreamLike, + readableStreamClose, + isomorphicEncode, + isomorphicDecode, + urlIsLocal, + urlHasHttpsScheme, + urlIsHttpHttpsScheme, + readAllBytes +} + + +/***/ }), + +/***/ 1744: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { types } = __nccwpck_require__(3837) +const { hasOwn, toUSVString } = __nccwpck_require__(2538) + +/** @type {import('../../types/webidl').Webidl} */ +const webidl = {} +webidl.converters = {} +webidl.util = {} +webidl.errors = {} + +webidl.errors.exception = function (message) { + return new TypeError(`${message.header}: ${message.message}`) +} + +webidl.errors.conversionFailed = function (context) { + const plural = context.types.length === 1 ? '' : ' one of' + const message = + `${context.argument} could not be converted to` + + `${plural}: ${context.types.join(', ')}.` + + return webidl.errors.exception({ + header: context.prefix, + message + }) +} + +webidl.errors.invalidArgument = function (context) { + return webidl.errors.exception({ + header: context.prefix, + message: `"${context.value}" is an invalid ${context.type}.` + }) +} + +// https://webidl.spec.whatwg.org/#implements +webidl.brandCheck = function (V, I, opts = undefined) { + if (opts?.strict !== false && !(V instanceof I)) { + throw new TypeError('Illegal invocation') + } else { + return V?.[Symbol.toStringTag] === I.prototype[Symbol.toStringTag] + } +} + +webidl.argumentLengthCheck = function ({ length }, min, ctx) { + if (length < min) { + throw webidl.errors.exception({ + message: `${min} argument${min !== 1 ? 's' : ''} required, ` + + `but${length ? ' only' : ''} ${length} found.`, + ...ctx + }) + } +} + +webidl.illegalConstructor = function () { + throw webidl.errors.exception({ + header: 'TypeError', + message: 'Illegal constructor' + }) +} + +// https://tc39.es/ecma262/#sec-ecmascript-data-types-and-values +webidl.util.Type = function (V) { + switch (typeof V) { + case 'undefined': return 'Undefined' + case 'boolean': return 'Boolean' + case 'string': return 'String' + case 'symbol': return 'Symbol' + case 'number': return 'Number' + case 'bigint': return 'BigInt' + case 'function': + case 'object': { + if (V === null) { + return 'Null' + } + + return 'Object' + } + } +} + +// https://webidl.spec.whatwg.org/#abstract-opdef-converttoint +webidl.util.ConvertToInt = function (V, bitLength, signedness, opts = {}) { + let upperBound + let lowerBound + + // 1. If bitLength is 64, then: + if (bitLength === 64) { + // 1. Let upperBound be 2^53 − 1. + upperBound = Math.pow(2, 53) - 1 + + // 2. If signedness is "unsigned", then let lowerBound be 0. + if (signedness === 'unsigned') { + lowerBound = 0 + } else { + // 3. Otherwise let lowerBound be −2^53 + 1. + lowerBound = Math.pow(-2, 53) + 1 + } + } else if (signedness === 'unsigned') { + // 2. Otherwise, if signedness is "unsigned", then: + + // 1. Let lowerBound be 0. + lowerBound = 0 + + // 2. Let upperBound be 2^bitLength − 1. + upperBound = Math.pow(2, bitLength) - 1 + } else { + // 3. Otherwise: + + // 1. Let lowerBound be -2^bitLength − 1. + lowerBound = Math.pow(-2, bitLength) - 1 + + // 2. Let upperBound be 2^bitLength − 1 − 1. + upperBound = Math.pow(2, bitLength - 1) - 1 + } + + // 4. Let x be ? ToNumber(V). + let x = Number(V) + + // 5. If x is −0, then set x to +0. + if (x === 0) { + x = 0 + } + + // 6. If the conversion is to an IDL type associated + // with the [EnforceRange] extended attribute, then: + if (opts.enforceRange === true) { + // 1. If x is NaN, +∞, or −∞, then throw a TypeError. + if ( + Number.isNaN(x) || + x === Number.POSITIVE_INFINITY || + x === Number.NEGATIVE_INFINITY + ) { + throw webidl.errors.exception({ + header: 'Integer conversion', + message: `Could not convert ${V} to an integer.` + }) + } + + // 2. Set x to IntegerPart(x). + x = webidl.util.IntegerPart(x) + + // 3. If x < lowerBound or x > upperBound, then + // throw a TypeError. + if (x < lowerBound || x > upperBound) { + throw webidl.errors.exception({ + header: 'Integer conversion', + message: `Value must be between ${lowerBound}-${upperBound}, got ${x}.` + }) + } + + // 4. Return x. + return x + } + + // 7. If x is not NaN and the conversion is to an IDL + // type associated with the [Clamp] extended + // attribute, then: + if (!Number.isNaN(x) && opts.clamp === true) { + // 1. Set x to min(max(x, lowerBound), upperBound). + x = Math.min(Math.max(x, lowerBound), upperBound) + + // 2. Round x to the nearest integer, choosing the + // even integer if it lies halfway between two, + // and choosing +0 rather than −0. + if (Math.floor(x) % 2 === 0) { + x = Math.floor(x) + } else { + x = Math.ceil(x) + } + + // 3. Return x. + return x + } + + // 8. If x is NaN, +0, +∞, or −∞, then return +0. + if ( + Number.isNaN(x) || + (x === 0 && Object.is(0, x)) || + x === Number.POSITIVE_INFINITY || + x === Number.NEGATIVE_INFINITY + ) { + return 0 + } + + // 9. Set x to IntegerPart(x). + x = webidl.util.IntegerPart(x) + + // 10. Set x to x modulo 2^bitLength. + x = x % Math.pow(2, bitLength) + + // 11. If signedness is "signed" and x ≥ 2^bitLength − 1, + // then return x − 2^bitLength. + if (signedness === 'signed' && x >= Math.pow(2, bitLength) - 1) { + return x - Math.pow(2, bitLength) + } + + // 12. Otherwise, return x. + return x +} + +// https://webidl.spec.whatwg.org/#abstract-opdef-integerpart +webidl.util.IntegerPart = function (n) { + // 1. Let r be floor(abs(n)). + const r = Math.floor(Math.abs(n)) + + // 2. If n < 0, then return -1 × r. + if (n < 0) { + return -1 * r + } + + // 3. Otherwise, return r. + return r +} + +// https://webidl.spec.whatwg.org/#es-sequence +webidl.sequenceConverter = function (converter) { + return (V) => { + // 1. If Type(V) is not Object, throw a TypeError. + if (webidl.util.Type(V) !== 'Object') { + throw webidl.errors.exception({ + header: 'Sequence', + message: `Value of type ${webidl.util.Type(V)} is not an Object.` + }) + } + + // 2. Let method be ? GetMethod(V, @@iterator). + /** @type {Generator} */ + const method = V?.[Symbol.iterator]?.() + const seq = [] + + // 3. If method is undefined, throw a TypeError. + if ( + method === undefined || + typeof method.next !== 'function' + ) { + throw webidl.errors.exception({ + header: 'Sequence', + message: 'Object is not an iterator.' + }) + } + + // https://webidl.spec.whatwg.org/#create-sequence-from-iterable + while (true) { + const { done, value } = method.next() + + if (done) { + break + } + + seq.push(converter(value)) + } + + return seq + } +} + +// https://webidl.spec.whatwg.org/#es-to-record +webidl.recordConverter = function (keyConverter, valueConverter) { + return (O) => { + // 1. If Type(O) is not Object, throw a TypeError. + if (webidl.util.Type(O) !== 'Object') { + throw webidl.errors.exception({ + header: 'Record', + message: `Value of type ${webidl.util.Type(O)} is not an Object.` + }) + } + + // 2. Let result be a new empty instance of record. + const result = {} + + if (!types.isProxy(O)) { + // Object.keys only returns enumerable properties + const keys = Object.keys(O) + + for (const key of keys) { + // 1. Let typedKey be key converted to an IDL value of type K. + const typedKey = keyConverter(key) + + // 2. Let value be ? Get(O, key). + // 3. Let typedValue be value converted to an IDL value of type V. + const typedValue = valueConverter(O[key]) + + // 4. Set result[typedKey] to typedValue. + result[typedKey] = typedValue + } + + // 5. Return result. + return result + } + + // 3. Let keys be ? O.[[OwnPropertyKeys]](). + const keys = Reflect.ownKeys(O) + + // 4. For each key of keys. + for (const key of keys) { + // 1. Let desc be ? O.[[GetOwnProperty]](key). + const desc = Reflect.getOwnPropertyDescriptor(O, key) + + // 2. If desc is not undefined and desc.[[Enumerable]] is true: + if (desc?.enumerable) { + // 1. Let typedKey be key converted to an IDL value of type K. + const typedKey = keyConverter(key) + + // 2. Let value be ? Get(O, key). + // 3. Let typedValue be value converted to an IDL value of type V. + const typedValue = valueConverter(O[key]) + + // 4. Set result[typedKey] to typedValue. + result[typedKey] = typedValue + } + } + + // 5. Return result. + return result + } +} + +webidl.interfaceConverter = function (i) { + return (V, opts = {}) => { + if (opts.strict !== false && !(V instanceof i)) { + throw webidl.errors.exception({ + header: i.name, + message: `Expected ${V} to be an instance of ${i.name}.` + }) + } + + return V + } +} + +webidl.dictionaryConverter = function (converters) { + return (dictionary) => { + const type = webidl.util.Type(dictionary) + const dict = {} + + if (type === 'Null' || type === 'Undefined') { + return dict + } else if (type !== 'Object') { + throw webidl.errors.exception({ + header: 'Dictionary', + message: `Expected ${dictionary} to be one of: Null, Undefined, Object.` + }) + } + + for (const options of converters) { + const { key, defaultValue, required, converter } = options + + if (required === true) { + if (!hasOwn(dictionary, key)) { + throw webidl.errors.exception({ + header: 'Dictionary', + message: `Missing required key "${key}".` + }) + } + } + + let value = dictionary[key] + const hasDefault = hasOwn(options, 'defaultValue') + + // Only use defaultValue if value is undefined and + // a defaultValue options was provided. + if (hasDefault && value !== null) { + value = value ?? defaultValue + } + + // A key can be optional and have no default value. + // When this happens, do not perform a conversion, + // and do not assign the key a value. + if (required || hasDefault || value !== undefined) { + value = converter(value) + + if ( + options.allowedValues && + !options.allowedValues.includes(value) + ) { + throw webidl.errors.exception({ + header: 'Dictionary', + message: `${value} is not an accepted type. Expected one of ${options.allowedValues.join(', ')}.` + }) + } + + dict[key] = value + } + } + + return dict + } +} + +webidl.nullableConverter = function (converter) { + return (V) => { + if (V === null) { + return V + } + + return converter(V) + } +} + +// https://webidl.spec.whatwg.org/#es-DOMString +webidl.converters.DOMString = function (V, opts = {}) { + // 1. If V is null and the conversion is to an IDL type + // associated with the [LegacyNullToEmptyString] + // extended attribute, then return the DOMString value + // that represents the empty string. + if (V === null && opts.legacyNullToEmptyString) { + return '' + } + + // 2. Let x be ? ToString(V). + if (typeof V === 'symbol') { + throw new TypeError('Could not convert argument of type symbol to string.') + } + + // 3. Return the IDL DOMString value that represents the + // same sequence of code units as the one the + // ECMAScript String value x represents. + return String(V) +} + +// https://webidl.spec.whatwg.org/#es-ByteString +webidl.converters.ByteString = function (V) { + // 1. Let x be ? ToString(V). + // Note: DOMString converter perform ? ToString(V) + const x = webidl.converters.DOMString(V) + + // 2. If the value of any element of x is greater than + // 255, then throw a TypeError. + for (let index = 0; index < x.length; index++) { + const charCode = x.charCodeAt(index) + + if (charCode > 255) { + throw new TypeError( + 'Cannot convert argument to a ByteString because the character at ' + + `index ${index} has a value of ${charCode} which is greater than 255.` + ) + } + } + + // 3. Return an IDL ByteString value whose length is the + // length of x, and where the value of each element is + // the value of the corresponding element of x. + return x +} + +// https://webidl.spec.whatwg.org/#es-USVString +webidl.converters.USVString = toUSVString + +// https://webidl.spec.whatwg.org/#es-boolean +webidl.converters.boolean = function (V) { + // 1. Let x be the result of computing ToBoolean(V). + const x = Boolean(V) + + // 2. Return the IDL boolean value that is the one that represents + // the same truth value as the ECMAScript Boolean value x. + return x +} + +// https://webidl.spec.whatwg.org/#es-any +webidl.converters.any = function (V) { + return V +} + +// https://webidl.spec.whatwg.org/#es-long-long +webidl.converters['long long'] = function (V) { + // 1. Let x be ? ConvertToInt(V, 64, "signed"). + const x = webidl.util.ConvertToInt(V, 64, 'signed') + + // 2. Return the IDL long long value that represents + // the same numeric value as x. + return x +} + +// https://webidl.spec.whatwg.org/#es-unsigned-long-long +webidl.converters['unsigned long long'] = function (V) { + // 1. Let x be ? ConvertToInt(V, 64, "unsigned"). + const x = webidl.util.ConvertToInt(V, 64, 'unsigned') + + // 2. Return the IDL unsigned long long value that + // represents the same numeric value as x. + return x +} + +// https://webidl.spec.whatwg.org/#es-unsigned-long +webidl.converters['unsigned long'] = function (V) { + // 1. Let x be ? ConvertToInt(V, 32, "unsigned"). + const x = webidl.util.ConvertToInt(V, 32, 'unsigned') + + // 2. Return the IDL unsigned long value that + // represents the same numeric value as x. + return x +} + +// https://webidl.spec.whatwg.org/#es-unsigned-short +webidl.converters['unsigned short'] = function (V, opts) { + // 1. Let x be ? ConvertToInt(V, 16, "unsigned"). + const x = webidl.util.ConvertToInt(V, 16, 'unsigned', opts) + + // 2. Return the IDL unsigned short value that represents + // the same numeric value as x. + return x +} + +// https://webidl.spec.whatwg.org/#idl-ArrayBuffer +webidl.converters.ArrayBuffer = function (V, opts = {}) { + // 1. If Type(V) is not Object, or V does not have an + // [[ArrayBufferData]] internal slot, then throw a + // TypeError. + // see: https://tc39.es/ecma262/#sec-properties-of-the-arraybuffer-instances + // see: https://tc39.es/ecma262/#sec-properties-of-the-sharedarraybuffer-instances + if ( + webidl.util.Type(V) !== 'Object' || + !types.isAnyArrayBuffer(V) + ) { + throw webidl.errors.conversionFailed({ + prefix: `${V}`, + argument: `${V}`, + types: ['ArrayBuffer'] + }) + } + + // 2. If the conversion is not to an IDL type associated + // with the [AllowShared] extended attribute, and + // IsSharedArrayBuffer(V) is true, then throw a + // TypeError. + if (opts.allowShared === false && types.isSharedArrayBuffer(V)) { + throw webidl.errors.exception({ + header: 'ArrayBuffer', + message: 'SharedArrayBuffer is not allowed.' + }) + } + + // 3. If the conversion is not to an IDL type associated + // with the [AllowResizable] extended attribute, and + // IsResizableArrayBuffer(V) is true, then throw a + // TypeError. + // Note: resizable ArrayBuffers are currently a proposal. + + // 4. Return the IDL ArrayBuffer value that is a + // reference to the same object as V. + return V +} + +webidl.converters.TypedArray = function (V, T, opts = {}) { + // 1. Let T be the IDL type V is being converted to. + + // 2. If Type(V) is not Object, or V does not have a + // [[TypedArrayName]] internal slot with a value + // equal to T’s name, then throw a TypeError. + if ( + webidl.util.Type(V) !== 'Object' || + !types.isTypedArray(V) || + V.constructor.name !== T.name + ) { + throw webidl.errors.conversionFailed({ + prefix: `${T.name}`, + argument: `${V}`, + types: [T.name] + }) + } + + // 3. If the conversion is not to an IDL type associated + // with the [AllowShared] extended attribute, and + // IsSharedArrayBuffer(V.[[ViewedArrayBuffer]]) is + // true, then throw a TypeError. + if (opts.allowShared === false && types.isSharedArrayBuffer(V.buffer)) { + throw webidl.errors.exception({ + header: 'ArrayBuffer', + message: 'SharedArrayBuffer is not allowed.' + }) + } + + // 4. If the conversion is not to an IDL type associated + // with the [AllowResizable] extended attribute, and + // IsResizableArrayBuffer(V.[[ViewedArrayBuffer]]) is + // true, then throw a TypeError. + // Note: resizable array buffers are currently a proposal + + // 5. Return the IDL value of type T that is a reference + // to the same object as V. + return V +} + +webidl.converters.DataView = function (V, opts = {}) { + // 1. If Type(V) is not Object, or V does not have a + // [[DataView]] internal slot, then throw a TypeError. + if (webidl.util.Type(V) !== 'Object' || !types.isDataView(V)) { + throw webidl.errors.exception({ + header: 'DataView', + message: 'Object is not a DataView.' + }) + } + + // 2. If the conversion is not to an IDL type associated + // with the [AllowShared] extended attribute, and + // IsSharedArrayBuffer(V.[[ViewedArrayBuffer]]) is true, + // then throw a TypeError. + if (opts.allowShared === false && types.isSharedArrayBuffer(V.buffer)) { + throw webidl.errors.exception({ + header: 'ArrayBuffer', + message: 'SharedArrayBuffer is not allowed.' + }) + } + + // 3. If the conversion is not to an IDL type associated + // with the [AllowResizable] extended attribute, and + // IsResizableArrayBuffer(V.[[ViewedArrayBuffer]]) is + // true, then throw a TypeError. + // Note: resizable ArrayBuffers are currently a proposal + + // 4. Return the IDL DataView value that is a reference + // to the same object as V. + return V +} + +// https://webidl.spec.whatwg.org/#BufferSource +webidl.converters.BufferSource = function (V, opts = {}) { + if (types.isAnyArrayBuffer(V)) { + return webidl.converters.ArrayBuffer(V, opts) + } + + if (types.isTypedArray(V)) { + return webidl.converters.TypedArray(V, V.constructor) + } + + if (types.isDataView(V)) { + return webidl.converters.DataView(V, opts) + } + + throw new TypeError(`Could not convert ${V} to a BufferSource.`) +} + +webidl.converters['sequence'] = webidl.sequenceConverter( + webidl.converters.ByteString +) + +webidl.converters['sequence>'] = webidl.sequenceConverter( + webidl.converters['sequence'] +) + +webidl.converters['record'] = webidl.recordConverter( + webidl.converters.ByteString, + webidl.converters.ByteString +) + +module.exports = { + webidl +} + + +/***/ }), + +/***/ 4854: +/***/ ((module) => { + +"use strict"; + + +/** + * @see https://encoding.spec.whatwg.org/#concept-encoding-get + * @param {string|undefined} label + */ +function getEncoding (label) { + if (!label) { + return 'failure' + } + + // 1. Remove any leading and trailing ASCII whitespace from label. + // 2. If label is an ASCII case-insensitive match for any of the + // labels listed in the table below, then return the + // corresponding encoding; otherwise return failure. + switch (label.trim().toLowerCase()) { + case 'unicode-1-1-utf-8': + case 'unicode11utf8': + case 'unicode20utf8': + case 'utf-8': + case 'utf8': + case 'x-unicode20utf8': + return 'UTF-8' + case '866': + case 'cp866': + case 'csibm866': + case 'ibm866': + return 'IBM866' + case 'csisolatin2': + case 'iso-8859-2': + case 'iso-ir-101': + case 'iso8859-2': + case 'iso88592': + case 'iso_8859-2': + case 'iso_8859-2:1987': + case 'l2': + case 'latin2': + return 'ISO-8859-2' + case 'csisolatin3': + case 'iso-8859-3': + case 'iso-ir-109': + case 'iso8859-3': + case 'iso88593': + case 'iso_8859-3': + case 'iso_8859-3:1988': + case 'l3': + case 'latin3': + return 'ISO-8859-3' + case 'csisolatin4': + case 'iso-8859-4': + case 'iso-ir-110': + case 'iso8859-4': + case 'iso88594': + case 'iso_8859-4': + case 'iso_8859-4:1988': + case 'l4': + case 'latin4': + return 'ISO-8859-4' + case 'csisolatincyrillic': + case 'cyrillic': + case 'iso-8859-5': + case 'iso-ir-144': + case 'iso8859-5': + case 'iso88595': + case 'iso_8859-5': + case 'iso_8859-5:1988': + return 'ISO-8859-5' + case 'arabic': + case 'asmo-708': + case 'csiso88596e': + case 'csiso88596i': + case 'csisolatinarabic': + case 'ecma-114': + case 'iso-8859-6': + case 'iso-8859-6-e': + case 'iso-8859-6-i': + case 'iso-ir-127': + case 'iso8859-6': + case 'iso88596': + case 'iso_8859-6': + case 'iso_8859-6:1987': + return 'ISO-8859-6' + case 'csisolatingreek': + case 'ecma-118': + case 'elot_928': + case 'greek': + case 'greek8': + case 'iso-8859-7': + case 'iso-ir-126': + case 'iso8859-7': + case 'iso88597': + case 'iso_8859-7': + case 'iso_8859-7:1987': + case 'sun_eu_greek': + return 'ISO-8859-7' + case 'csiso88598e': + case 'csisolatinhebrew': + case 'hebrew': + case 'iso-8859-8': + case 'iso-8859-8-e': + case 'iso-ir-138': + case 'iso8859-8': + case 'iso88598': + case 'iso_8859-8': + case 'iso_8859-8:1988': + case 'visual': + return 'ISO-8859-8' + case 'csiso88598i': + case 'iso-8859-8-i': + case 'logical': + return 'ISO-8859-8-I' + case 'csisolatin6': + case 'iso-8859-10': + case 'iso-ir-157': + case 'iso8859-10': + case 'iso885910': + case 'l6': + case 'latin6': + return 'ISO-8859-10' + case 'iso-8859-13': + case 'iso8859-13': + case 'iso885913': + return 'ISO-8859-13' + case 'iso-8859-14': + case 'iso8859-14': + case 'iso885914': + return 'ISO-8859-14' + case 'csisolatin9': + case 'iso-8859-15': + case 'iso8859-15': + case 'iso885915': + case 'iso_8859-15': + case 'l9': + return 'ISO-8859-15' + case 'iso-8859-16': + return 'ISO-8859-16' + case 'cskoi8r': + case 'koi': + case 'koi8': + case 'koi8-r': + case 'koi8_r': + return 'KOI8-R' + case 'koi8-ru': + case 'koi8-u': + return 'KOI8-U' + case 'csmacintosh': + case 'mac': + case 'macintosh': + case 'x-mac-roman': + return 'macintosh' + case 'iso-8859-11': + case 'iso8859-11': + case 'iso885911': + case 'tis-620': + case 'windows-874': + return 'windows-874' + case 'cp1250': + case 'windows-1250': + case 'x-cp1250': + return 'windows-1250' + case 'cp1251': + case 'windows-1251': + case 'x-cp1251': + return 'windows-1251' + case 'ansi_x3.4-1968': + case 'ascii': + case 'cp1252': + case 'cp819': + case 'csisolatin1': + case 'ibm819': + case 'iso-8859-1': + case 'iso-ir-100': + case 'iso8859-1': + case 'iso88591': + case 'iso_8859-1': + case 'iso_8859-1:1987': + case 'l1': + case 'latin1': + case 'us-ascii': + case 'windows-1252': + case 'x-cp1252': + return 'windows-1252' + case 'cp1253': + case 'windows-1253': + case 'x-cp1253': + return 'windows-1253' + case 'cp1254': + case 'csisolatin5': + case 'iso-8859-9': + case 'iso-ir-148': + case 'iso8859-9': + case 'iso88599': + case 'iso_8859-9': + case 'iso_8859-9:1989': + case 'l5': + case 'latin5': + case 'windows-1254': + case 'x-cp1254': + return 'windows-1254' + case 'cp1255': + case 'windows-1255': + case 'x-cp1255': + return 'windows-1255' + case 'cp1256': + case 'windows-1256': + case 'x-cp1256': + return 'windows-1256' + case 'cp1257': + case 'windows-1257': + case 'x-cp1257': + return 'windows-1257' + case 'cp1258': + case 'windows-1258': + case 'x-cp1258': + return 'windows-1258' + case 'x-mac-cyrillic': + case 'x-mac-ukrainian': + return 'x-mac-cyrillic' + case 'chinese': + case 'csgb2312': + case 'csiso58gb231280': + case 'gb2312': + case 'gb_2312': + case 'gb_2312-80': + case 'gbk': + case 'iso-ir-58': + case 'x-gbk': + return 'GBK' + case 'gb18030': + return 'gb18030' + case 'big5': + case 'big5-hkscs': + case 'cn-big5': + case 'csbig5': + case 'x-x-big5': + return 'Big5' + case 'cseucpkdfmtjapanese': + case 'euc-jp': + case 'x-euc-jp': + return 'EUC-JP' + case 'csiso2022jp': + case 'iso-2022-jp': + return 'ISO-2022-JP' + case 'csshiftjis': + case 'ms932': + case 'ms_kanji': + case 'shift-jis': + case 'shift_jis': + case 'sjis': + case 'windows-31j': + case 'x-sjis': + return 'Shift_JIS' + case 'cseuckr': + case 'csksc56011987': + case 'euc-kr': + case 'iso-ir-149': + case 'korean': + case 'ks_c_5601-1987': + case 'ks_c_5601-1989': + case 'ksc5601': + case 'ksc_5601': + case 'windows-949': + return 'EUC-KR' + case 'csiso2022kr': + case 'hz-gb-2312': + case 'iso-2022-cn': + case 'iso-2022-cn-ext': + case 'iso-2022-kr': + case 'replacement': + return 'replacement' + case 'unicodefffe': + case 'utf-16be': + return 'UTF-16BE' + case 'csunicode': + case 'iso-10646-ucs-2': + case 'ucs-2': + case 'unicode': + case 'unicodefeff': + case 'utf-16': + case 'utf-16le': + return 'UTF-16LE' + case 'x-user-defined': + return 'x-user-defined' + default: return 'failure' + } +} + +module.exports = { + getEncoding +} + + +/***/ }), + +/***/ 1446: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { + staticPropertyDescriptors, + readOperation, + fireAProgressEvent +} = __nccwpck_require__(7530) +const { + kState, + kError, + kResult, + kEvents, + kAborted +} = __nccwpck_require__(9054) +const { webidl } = __nccwpck_require__(1744) +const { kEnumerableProperty } = __nccwpck_require__(3983) + +class FileReader extends EventTarget { + constructor () { + super() + + this[kState] = 'empty' + this[kResult] = null + this[kError] = null + this[kEvents] = { + loadend: null, + error: null, + abort: null, + load: null, + progress: null, + loadstart: null + } + } + + /** + * @see https://w3c.github.io/FileAPI/#dfn-readAsArrayBuffer + * @param {import('buffer').Blob} blob + */ + readAsArrayBuffer (blob) { + webidl.brandCheck(this, FileReader) + + webidl.argumentLengthCheck(arguments, 1, { header: 'FileReader.readAsArrayBuffer' }) + + blob = webidl.converters.Blob(blob, { strict: false }) + + // The readAsArrayBuffer(blob) method, when invoked, + // must initiate a read operation for blob with ArrayBuffer. + readOperation(this, blob, 'ArrayBuffer') + } + + /** + * @see https://w3c.github.io/FileAPI/#readAsBinaryString + * @param {import('buffer').Blob} blob + */ + readAsBinaryString (blob) { + webidl.brandCheck(this, FileReader) + + webidl.argumentLengthCheck(arguments, 1, { header: 'FileReader.readAsBinaryString' }) + + blob = webidl.converters.Blob(blob, { strict: false }) + + // The readAsBinaryString(blob) method, when invoked, + // must initiate a read operation for blob with BinaryString. + readOperation(this, blob, 'BinaryString') + } + + /** + * @see https://w3c.github.io/FileAPI/#readAsDataText + * @param {import('buffer').Blob} blob + * @param {string?} encoding + */ + readAsText (blob, encoding = undefined) { + webidl.brandCheck(this, FileReader) + + webidl.argumentLengthCheck(arguments, 1, { header: 'FileReader.readAsText' }) + + blob = webidl.converters.Blob(blob, { strict: false }) + + if (encoding !== undefined) { + encoding = webidl.converters.DOMString(encoding) + } + + // The readAsText(blob, encoding) method, when invoked, + // must initiate a read operation for blob with Text and encoding. + readOperation(this, blob, 'Text', encoding) + } + + /** + * @see https://w3c.github.io/FileAPI/#dfn-readAsDataURL + * @param {import('buffer').Blob} blob + */ + readAsDataURL (blob) { + webidl.brandCheck(this, FileReader) + + webidl.argumentLengthCheck(arguments, 1, { header: 'FileReader.readAsDataURL' }) + + blob = webidl.converters.Blob(blob, { strict: false }) + + // The readAsDataURL(blob) method, when invoked, must + // initiate a read operation for blob with DataURL. + readOperation(this, blob, 'DataURL') + } + + /** + * @see https://w3c.github.io/FileAPI/#dfn-abort + */ + abort () { + // 1. If this's state is "empty" or if this's state is + // "done" set this's result to null and terminate + // this algorithm. + if (this[kState] === 'empty' || this[kState] === 'done') { + this[kResult] = null + return + } + + // 2. If this's state is "loading" set this's state to + // "done" and set this's result to null. + if (this[kState] === 'loading') { + this[kState] = 'done' + this[kResult] = null + } + + // 3. If there are any tasks from this on the file reading + // task source in an affiliated task queue, then remove + // those tasks from that task queue. + this[kAborted] = true + + // 4. Terminate the algorithm for the read method being processed. + // TODO + + // 5. Fire a progress event called abort at this. + fireAProgressEvent('abort', this) + + // 6. If this's state is not "loading", fire a progress + // event called loadend at this. + if (this[kState] !== 'loading') { + fireAProgressEvent('loadend', this) + } + } + + /** + * @see https://w3c.github.io/FileAPI/#dom-filereader-readystate + */ + get readyState () { + webidl.brandCheck(this, FileReader) + + switch (this[kState]) { + case 'empty': return this.EMPTY + case 'loading': return this.LOADING + case 'done': return this.DONE + } + } + + /** + * @see https://w3c.github.io/FileAPI/#dom-filereader-result + */ + get result () { + webidl.brandCheck(this, FileReader) + + // The result attribute’s getter, when invoked, must return + // this's result. + return this[kResult] + } + + /** + * @see https://w3c.github.io/FileAPI/#dom-filereader-error + */ + get error () { + webidl.brandCheck(this, FileReader) + + // The error attribute’s getter, when invoked, must return + // this's error. + return this[kError] + } + + get onloadend () { + webidl.brandCheck(this, FileReader) + + return this[kEvents].loadend + } + + set onloadend (fn) { + webidl.brandCheck(this, FileReader) + + if (this[kEvents].loadend) { + this.removeEventListener('loadend', this[kEvents].loadend) + } + + if (typeof fn === 'function') { + this[kEvents].loadend = fn + this.addEventListener('loadend', fn) + } else { + this[kEvents].loadend = null + } + } + + get onerror () { + webidl.brandCheck(this, FileReader) + + return this[kEvents].error + } + + set onerror (fn) { + webidl.brandCheck(this, FileReader) + + if (this[kEvents].error) { + this.removeEventListener('error', this[kEvents].error) + } + + if (typeof fn === 'function') { + this[kEvents].error = fn + this.addEventListener('error', fn) + } else { + this[kEvents].error = null + } + } + + get onloadstart () { + webidl.brandCheck(this, FileReader) + + return this[kEvents].loadstart + } + + set onloadstart (fn) { + webidl.brandCheck(this, FileReader) + + if (this[kEvents].loadstart) { + this.removeEventListener('loadstart', this[kEvents].loadstart) + } + + if (typeof fn === 'function') { + this[kEvents].loadstart = fn + this.addEventListener('loadstart', fn) + } else { + this[kEvents].loadstart = null + } + } + + get onprogress () { + webidl.brandCheck(this, FileReader) + + return this[kEvents].progress + } + + set onprogress (fn) { + webidl.brandCheck(this, FileReader) + + if (this[kEvents].progress) { + this.removeEventListener('progress', this[kEvents].progress) + } + + if (typeof fn === 'function') { + this[kEvents].progress = fn + this.addEventListener('progress', fn) + } else { + this[kEvents].progress = null + } + } + + get onload () { + webidl.brandCheck(this, FileReader) + + return this[kEvents].load + } + + set onload (fn) { + webidl.brandCheck(this, FileReader) + + if (this[kEvents].load) { + this.removeEventListener('load', this[kEvents].load) + } + + if (typeof fn === 'function') { + this[kEvents].load = fn + this.addEventListener('load', fn) + } else { + this[kEvents].load = null + } + } + + get onabort () { + webidl.brandCheck(this, FileReader) + + return this[kEvents].abort + } + + set onabort (fn) { + webidl.brandCheck(this, FileReader) + + if (this[kEvents].abort) { + this.removeEventListener('abort', this[kEvents].abort) + } + + if (typeof fn === 'function') { + this[kEvents].abort = fn + this.addEventListener('abort', fn) + } else { + this[kEvents].abort = null + } + } +} + +// https://w3c.github.io/FileAPI/#dom-filereader-empty +FileReader.EMPTY = FileReader.prototype.EMPTY = 0 +// https://w3c.github.io/FileAPI/#dom-filereader-loading +FileReader.LOADING = FileReader.prototype.LOADING = 1 +// https://w3c.github.io/FileAPI/#dom-filereader-done +FileReader.DONE = FileReader.prototype.DONE = 2 + +Object.defineProperties(FileReader.prototype, { + EMPTY: staticPropertyDescriptors, + LOADING: staticPropertyDescriptors, + DONE: staticPropertyDescriptors, + readAsArrayBuffer: kEnumerableProperty, + readAsBinaryString: kEnumerableProperty, + readAsText: kEnumerableProperty, + readAsDataURL: kEnumerableProperty, + abort: kEnumerableProperty, + readyState: kEnumerableProperty, + result: kEnumerableProperty, + error: kEnumerableProperty, + onloadstart: kEnumerableProperty, + onprogress: kEnumerableProperty, + onload: kEnumerableProperty, + onabort: kEnumerableProperty, + onerror: kEnumerableProperty, + onloadend: kEnumerableProperty, + [Symbol.toStringTag]: { + value: 'FileReader', + writable: false, + enumerable: false, + configurable: true + } +}) + +Object.defineProperties(FileReader, { + EMPTY: staticPropertyDescriptors, + LOADING: staticPropertyDescriptors, + DONE: staticPropertyDescriptors +}) + +module.exports = { + FileReader +} + + +/***/ }), + +/***/ 5504: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { webidl } = __nccwpck_require__(1744) + +const kState = Symbol('ProgressEvent state') + +/** + * @see https://xhr.spec.whatwg.org/#progressevent + */ +class ProgressEvent extends Event { + constructor (type, eventInitDict = {}) { + type = webidl.converters.DOMString(type) + eventInitDict = webidl.converters.ProgressEventInit(eventInitDict ?? {}) + + super(type, eventInitDict) + + this[kState] = { + lengthComputable: eventInitDict.lengthComputable, + loaded: eventInitDict.loaded, + total: eventInitDict.total + } + } + + get lengthComputable () { + webidl.brandCheck(this, ProgressEvent) + + return this[kState].lengthComputable + } + + get loaded () { + webidl.brandCheck(this, ProgressEvent) + + return this[kState].loaded + } + + get total () { + webidl.brandCheck(this, ProgressEvent) + + return this[kState].total + } +} + +webidl.converters.ProgressEventInit = webidl.dictionaryConverter([ + { + key: 'lengthComputable', + converter: webidl.converters.boolean, + defaultValue: false + }, + { + key: 'loaded', + converter: webidl.converters['unsigned long long'], + defaultValue: 0 + }, + { + key: 'total', + converter: webidl.converters['unsigned long long'], + defaultValue: 0 + }, + { + key: 'bubbles', + converter: webidl.converters.boolean, + defaultValue: false + }, + { + key: 'cancelable', + converter: webidl.converters.boolean, + defaultValue: false + }, + { + key: 'composed', + converter: webidl.converters.boolean, + defaultValue: false + } +]) + +module.exports = { + ProgressEvent +} + + +/***/ }), + +/***/ 9054: +/***/ ((module) => { + +"use strict"; + + +module.exports = { + kState: Symbol('FileReader state'), + kResult: Symbol('FileReader result'), + kError: Symbol('FileReader error'), + kLastProgressEventFired: Symbol('FileReader last progress event fired timestamp'), + kEvents: Symbol('FileReader events'), + kAborted: Symbol('FileReader aborted') +} + + +/***/ }), + +/***/ 7530: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { + kState, + kError, + kResult, + kAborted, + kLastProgressEventFired +} = __nccwpck_require__(9054) +const { ProgressEvent } = __nccwpck_require__(5504) +const { getEncoding } = __nccwpck_require__(4854) +const { DOMException } = __nccwpck_require__(1037) +const { serializeAMimeType, parseMIMEType } = __nccwpck_require__(685) +const { types } = __nccwpck_require__(3837) +const { StringDecoder } = __nccwpck_require__(1576) +const { btoa } = __nccwpck_require__(4300) + +/** @type {PropertyDescriptor} */ +const staticPropertyDescriptors = { + enumerable: true, + writable: false, + configurable: false +} + +/** + * @see https://w3c.github.io/FileAPI/#readOperation + * @param {import('./filereader').FileReader} fr + * @param {import('buffer').Blob} blob + * @param {string} type + * @param {string?} encodingName + */ +function readOperation (fr, blob, type, encodingName) { + // 1. If fr’s state is "loading", throw an InvalidStateError + // DOMException. + if (fr[kState] === 'loading') { + throw new DOMException('Invalid state', 'InvalidStateError') + } + + // 2. Set fr’s state to "loading". + fr[kState] = 'loading' + + // 3. Set fr’s result to null. + fr[kResult] = null + + // 4. Set fr’s error to null. + fr[kError] = null + + // 5. Let stream be the result of calling get stream on blob. + /** @type {import('stream/web').ReadableStream} */ + const stream = blob.stream() + + // 6. Let reader be the result of getting a reader from stream. + const reader = stream.getReader() + + // 7. Let bytes be an empty byte sequence. + /** @type {Uint8Array[]} */ + const bytes = [] + + // 8. Let chunkPromise be the result of reading a chunk from + // stream with reader. + let chunkPromise = reader.read() + + // 9. Let isFirstChunk be true. + let isFirstChunk = true + + // 10. In parallel, while true: + // Note: "In parallel" just means non-blocking + // Note 2: readOperation itself cannot be async as double + // reading the body would then reject the promise, instead + // of throwing an error. + ;(async () => { + while (!fr[kAborted]) { + // 1. Wait for chunkPromise to be fulfilled or rejected. + try { + const { done, value } = await chunkPromise + + // 2. If chunkPromise is fulfilled, and isFirstChunk is + // true, queue a task to fire a progress event called + // loadstart at fr. + if (isFirstChunk && !fr[kAborted]) { + queueMicrotask(() => { + fireAProgressEvent('loadstart', fr) + }) + } + + // 3. Set isFirstChunk to false. + isFirstChunk = false + + // 4. If chunkPromise is fulfilled with an object whose + // done property is false and whose value property is + // a Uint8Array object, run these steps: + if (!done && types.isUint8Array(value)) { + // 1. Let bs be the byte sequence represented by the + // Uint8Array object. + + // 2. Append bs to bytes. + bytes.push(value) + + // 3. If roughly 50ms have passed since these steps + // were last invoked, queue a task to fire a + // progress event called progress at fr. + if ( + ( + fr[kLastProgressEventFired] === undefined || + Date.now() - fr[kLastProgressEventFired] >= 50 + ) && + !fr[kAborted] + ) { + fr[kLastProgressEventFired] = Date.now() + queueMicrotask(() => { + fireAProgressEvent('progress', fr) + }) + } + + // 4. Set chunkPromise to the result of reading a + // chunk from stream with reader. + chunkPromise = reader.read() + } else if (done) { + // 5. Otherwise, if chunkPromise is fulfilled with an + // object whose done property is true, queue a task + // to run the following steps and abort this algorithm: + queueMicrotask(() => { + // 1. Set fr’s state to "done". + fr[kState] = 'done' + + // 2. Let result be the result of package data given + // bytes, type, blob’s type, and encodingName. + try { + const result = packageData(bytes, type, blob.type, encodingName) + + // 4. Else: + + if (fr[kAborted]) { + return + } + + // 1. Set fr’s result to result. + fr[kResult] = result + + // 2. Fire a progress event called load at the fr. + fireAProgressEvent('load', fr) + } catch (error) { + // 3. If package data threw an exception error: + + // 1. Set fr’s error to error. + fr[kError] = error + + // 2. Fire a progress event called error at fr. + fireAProgressEvent('error', fr) + } + + // 5. If fr’s state is not "loading", fire a progress + // event called loadend at the fr. + if (fr[kState] !== 'loading') { + fireAProgressEvent('loadend', fr) + } + }) + + break + } + } catch (error) { + if (fr[kAborted]) { + return + } + + // 6. Otherwise, if chunkPromise is rejected with an + // error error, queue a task to run the following + // steps and abort this algorithm: + queueMicrotask(() => { + // 1. Set fr’s state to "done". + fr[kState] = 'done' + + // 2. Set fr’s error to error. + fr[kError] = error + + // 3. Fire a progress event called error at fr. + fireAProgressEvent('error', fr) + + // 4. If fr’s state is not "loading", fire a progress + // event called loadend at fr. + if (fr[kState] !== 'loading') { + fireAProgressEvent('loadend', fr) + } + }) + + break + } + } + })() +} + +/** + * @see https://w3c.github.io/FileAPI/#fire-a-progress-event + * @see https://dom.spec.whatwg.org/#concept-event-fire + * @param {string} e The name of the event + * @param {import('./filereader').FileReader} reader + */ +function fireAProgressEvent (e, reader) { + // The progress event e does not bubble. e.bubbles must be false + // The progress event e is NOT cancelable. e.cancelable must be false + const event = new ProgressEvent(e, { + bubbles: false, + cancelable: false + }) + + reader.dispatchEvent(event) +} + +/** + * @see https://w3c.github.io/FileAPI/#blob-package-data + * @param {Uint8Array[]} bytes + * @param {string} type + * @param {string?} mimeType + * @param {string?} encodingName + */ +function packageData (bytes, type, mimeType, encodingName) { + // 1. A Blob has an associated package data algorithm, given + // bytes, a type, a optional mimeType, and a optional + // encodingName, which switches on type and runs the + // associated steps: + + switch (type) { + case 'DataURL': { + // 1. Return bytes as a DataURL [RFC2397] subject to + // the considerations below: + // * Use mimeType as part of the Data URL if it is + // available in keeping with the Data URL + // specification [RFC2397]. + // * If mimeType is not available return a Data URL + // without a media-type. [RFC2397]. + + // https://datatracker.ietf.org/doc/html/rfc2397#section-3 + // dataurl := "data:" [ mediatype ] [ ";base64" ] "," data + // mediatype := [ type "/" subtype ] *( ";" parameter ) + // data := *urlchar + // parameter := attribute "=" value + let dataURL = 'data:' + + const parsed = parseMIMEType(mimeType || 'application/octet-stream') + + if (parsed !== 'failure') { + dataURL += serializeAMimeType(parsed) + } + + dataURL += ';base64,' + + const decoder = new StringDecoder('latin1') + + for (const chunk of bytes) { + dataURL += btoa(decoder.write(chunk)) + } + + dataURL += btoa(decoder.end()) + + return dataURL + } + case 'Text': { + // 1. Let encoding be failure + let encoding = 'failure' + + // 2. If the encodingName is present, set encoding to the + // result of getting an encoding from encodingName. + if (encodingName) { + encoding = getEncoding(encodingName) + } + + // 3. If encoding is failure, and mimeType is present: + if (encoding === 'failure' && mimeType) { + // 1. Let type be the result of parse a MIME type + // given mimeType. + const type = parseMIMEType(mimeType) + + // 2. If type is not failure, set encoding to the result + // of getting an encoding from type’s parameters["charset"]. + if (type !== 'failure') { + encoding = getEncoding(type.parameters.get('charset')) + } + } + + // 4. If encoding is failure, then set encoding to UTF-8. + if (encoding === 'failure') { + encoding = 'UTF-8' + } + + // 5. Decode bytes using fallback encoding encoding, and + // return the result. + return decode(bytes, encoding) + } + case 'ArrayBuffer': { + // Return a new ArrayBuffer whose contents are bytes. + const sequence = combineByteSequences(bytes) + + return sequence.buffer + } + case 'BinaryString': { + // Return bytes as a binary string, in which every byte + // is represented by a code unit of equal value [0..255]. + let binaryString = '' + + const decoder = new StringDecoder('latin1') + + for (const chunk of bytes) { + binaryString += decoder.write(chunk) + } + + binaryString += decoder.end() + + return binaryString + } + } +} + +/** + * @see https://encoding.spec.whatwg.org/#decode + * @param {Uint8Array[]} ioQueue + * @param {string} encoding + */ +function decode (ioQueue, encoding) { + const bytes = combineByteSequences(ioQueue) + + // 1. Let BOMEncoding be the result of BOM sniffing ioQueue. + const BOMEncoding = BOMSniffing(bytes) + + let slice = 0 + + // 2. If BOMEncoding is non-null: + if (BOMEncoding !== null) { + // 1. Set encoding to BOMEncoding. + encoding = BOMEncoding + + // 2. Read three bytes from ioQueue, if BOMEncoding is + // UTF-8; otherwise read two bytes. + // (Do nothing with those bytes.) + slice = BOMEncoding === 'UTF-8' ? 3 : 2 + } + + // 3. Process a queue with an instance of encoding’s + // decoder, ioQueue, output, and "replacement". + + // 4. Return output. + + const sliced = bytes.slice(slice) + return new TextDecoder(encoding).decode(sliced) +} + +/** + * @see https://encoding.spec.whatwg.org/#bom-sniff + * @param {Uint8Array} ioQueue + */ +function BOMSniffing (ioQueue) { + // 1. Let BOM be the result of peeking 3 bytes from ioQueue, + // converted to a byte sequence. + const [a, b, c] = ioQueue + + // 2. For each of the rows in the table below, starting with + // the first one and going down, if BOM starts with the + // bytes given in the first column, then return the + // encoding given in the cell in the second column of that + // row. Otherwise, return null. + if (a === 0xEF && b === 0xBB && c === 0xBF) { + return 'UTF-8' + } else if (a === 0xFE && b === 0xFF) { + return 'UTF-16BE' + } else if (a === 0xFF && b === 0xFE) { + return 'UTF-16LE' + } + + return null +} + +/** + * @param {Uint8Array[]} sequences + */ +function combineByteSequences (sequences) { + const size = sequences.reduce((a, b) => { + return a + b.byteLength + }, 0) + + let offset = 0 + + return sequences.reduce((a, b) => { + a.set(b, offset) + offset += b.byteLength + return a + }, new Uint8Array(size)) +} + +module.exports = { + staticPropertyDescriptors, + readOperation, + fireAProgressEvent +} + + +/***/ }), + +/***/ 1892: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +// We include a version number for the Dispatcher API. In case of breaking changes, +// this version number must be increased to avoid conflicts. +const globalDispatcher = Symbol.for('undici.globalDispatcher.1') +const { InvalidArgumentError } = __nccwpck_require__(8045) +const Agent = __nccwpck_require__(7890) + +if (getGlobalDispatcher() === undefined) { + setGlobalDispatcher(new Agent()) +} + +function setGlobalDispatcher (agent) { + if (!agent || typeof agent.dispatch !== 'function') { + throw new InvalidArgumentError('Argument agent must implement Agent') + } + Object.defineProperty(globalThis, globalDispatcher, { + value: agent, + writable: true, + enumerable: false, + configurable: false + }) +} + +function getGlobalDispatcher () { + return globalThis[globalDispatcher] +} + +module.exports = { + setGlobalDispatcher, + getGlobalDispatcher +} + + +/***/ }), + +/***/ 6930: +/***/ ((module) => { + +"use strict"; + + +module.exports = class DecoratorHandler { + constructor (handler) { + this.handler = handler + } + + onConnect (...args) { + return this.handler.onConnect(...args) + } + + onError (...args) { + return this.handler.onError(...args) + } + + onUpgrade (...args) { + return this.handler.onUpgrade(...args) + } + + onHeaders (...args) { + return this.handler.onHeaders(...args) + } + + onData (...args) { + return this.handler.onData(...args) + } + + onComplete (...args) { + return this.handler.onComplete(...args) + } + + onBodySent (...args) { + return this.handler.onBodySent(...args) + } +} + + +/***/ }), + +/***/ 2860: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const util = __nccwpck_require__(3983) +const { kBodyUsed } = __nccwpck_require__(2785) +const assert = __nccwpck_require__(9491) +const { InvalidArgumentError } = __nccwpck_require__(8045) +const EE = __nccwpck_require__(2361) + +const redirectableStatusCodes = [300, 301, 302, 303, 307, 308] + +const kBody = Symbol('body') + +class BodyAsyncIterable { + constructor (body) { + this[kBody] = body + this[kBodyUsed] = false + } + + async * [Symbol.asyncIterator] () { + assert(!this[kBodyUsed], 'disturbed') + this[kBodyUsed] = true + yield * this[kBody] + } +} + +class RedirectHandler { + constructor (dispatch, maxRedirections, opts, handler) { + if (maxRedirections != null && (!Number.isInteger(maxRedirections) || maxRedirections < 0)) { + throw new InvalidArgumentError('maxRedirections must be a positive number') + } + + util.validateHandler(handler, opts.method, opts.upgrade) + + this.dispatch = dispatch + this.location = null + this.abort = null + this.opts = { ...opts, maxRedirections: 0 } // opts must be a copy + this.maxRedirections = maxRedirections + this.handler = handler + this.history = [] + + if (util.isStream(this.opts.body)) { + // TODO (fix): Provide some way for the user to cache the file to e.g. /tmp + // so that it can be dispatched again? + // TODO (fix): Do we need 100-expect support to provide a way to do this properly? + if (util.bodyLength(this.opts.body) === 0) { + this.opts.body + .on('data', function () { + assert(false) + }) + } + + if (typeof this.opts.body.readableDidRead !== 'boolean') { + this.opts.body[kBodyUsed] = false + EE.prototype.on.call(this.opts.body, 'data', function () { + this[kBodyUsed] = true + }) + } + } else if (this.opts.body && typeof this.opts.body.pipeTo === 'function') { + // TODO (fix): We can't access ReadableStream internal state + // to determine whether or not it has been disturbed. This is just + // a workaround. + this.opts.body = new BodyAsyncIterable(this.opts.body) + } else if ( + this.opts.body && + typeof this.opts.body !== 'string' && + !ArrayBuffer.isView(this.opts.body) && + util.isIterable(this.opts.body) + ) { + // TODO: Should we allow re-using iterable if !this.opts.idempotent + // or through some other flag? + this.opts.body = new BodyAsyncIterable(this.opts.body) + } + } + + onConnect (abort) { + this.abort = abort + this.handler.onConnect(abort, { history: this.history }) + } + + onUpgrade (statusCode, headers, socket) { + this.handler.onUpgrade(statusCode, headers, socket) + } + + onError (error) { + this.handler.onError(error) + } + + onHeaders (statusCode, headers, resume, statusText) { + this.location = this.history.length >= this.maxRedirections || util.isDisturbed(this.opts.body) + ? null + : parseLocation(statusCode, headers) + + if (this.opts.origin) { + this.history.push(new URL(this.opts.path, this.opts.origin)) + } + + if (!this.location) { + return this.handler.onHeaders(statusCode, headers, resume, statusText) + } + + const { origin, pathname, search } = util.parseURL(new URL(this.location, this.opts.origin && new URL(this.opts.path, this.opts.origin))) + const path = search ? `${pathname}${search}` : pathname + + // Remove headers referring to the original URL. + // By default it is Host only, unless it's a 303 (see below), which removes also all Content-* headers. + // https://tools.ietf.org/html/rfc7231#section-6.4 + this.opts.headers = cleanRequestHeaders(this.opts.headers, statusCode === 303, this.opts.origin !== origin) + this.opts.path = path + this.opts.origin = origin + this.opts.maxRedirections = 0 + this.opts.query = null + + // https://tools.ietf.org/html/rfc7231#section-6.4.4 + // In case of HTTP 303, always replace method to be either HEAD or GET + if (statusCode === 303 && this.opts.method !== 'HEAD') { + this.opts.method = 'GET' + this.opts.body = null + } + } + + onData (chunk) { + if (this.location) { + /* + https://tools.ietf.org/html/rfc7231#section-6.4 + + TLDR: undici always ignores 3xx response bodies. + + Redirection is used to serve the requested resource from another URL, so it is assumes that + no body is generated (and thus can be ignored). Even though generating a body is not prohibited. + + For status 301, 302, 303, 307 and 308 (the latter from RFC 7238), the specs mention that the body usually + (which means it's optional and not mandated) contain just an hyperlink to the value of + the Location response header, so the body can be ignored safely. + + For status 300, which is "Multiple Choices", the spec mentions both generating a Location + response header AND a response body with the other possible location to follow. + Since the spec explicitily chooses not to specify a format for such body and leave it to + servers and browsers implementors, we ignore the body as there is no specified way to eventually parse it. + */ + } else { + return this.handler.onData(chunk) + } + } + + onComplete (trailers) { + if (this.location) { + /* + https://tools.ietf.org/html/rfc7231#section-6.4 + + TLDR: undici always ignores 3xx response trailers as they are not expected in case of redirections + and neither are useful if present. + + See comment on onData method above for more detailed informations. + */ + + this.location = null + this.abort = null + + this.dispatch(this.opts, this) + } else { + this.handler.onComplete(trailers) + } + } + + onBodySent (chunk) { + if (this.handler.onBodySent) { + this.handler.onBodySent(chunk) + } + } +} + +function parseLocation (statusCode, headers) { + if (redirectableStatusCodes.indexOf(statusCode) === -1) { + return null + } + + for (let i = 0; i < headers.length; i += 2) { + if (headers[i].toString().toLowerCase() === 'location') { + return headers[i + 1] + } + } +} + +// https://tools.ietf.org/html/rfc7231#section-6.4.4 +function shouldRemoveHeader (header, removeContent, unknownOrigin) { + return ( + (header.length === 4 && header.toString().toLowerCase() === 'host') || + (removeContent && header.toString().toLowerCase().indexOf('content-') === 0) || + (unknownOrigin && header.length === 13 && header.toString().toLowerCase() === 'authorization') || + (unknownOrigin && header.length === 6 && header.toString().toLowerCase() === 'cookie') + ) +} + +// https://tools.ietf.org/html/rfc7231#section-6.4 +function cleanRequestHeaders (headers, removeContent, unknownOrigin) { + const ret = [] + if (Array.isArray(headers)) { + for (let i = 0; i < headers.length; i += 2) { + if (!shouldRemoveHeader(headers[i], removeContent, unknownOrigin)) { + ret.push(headers[i], headers[i + 1]) + } + } + } else if (headers && typeof headers === 'object') { + for (const key of Object.keys(headers)) { + if (!shouldRemoveHeader(key, removeContent, unknownOrigin)) { + ret.push(key, headers[key]) + } + } + } else { + assert(headers == null, 'headers must be an object or an array') + } + return ret +} + +module.exports = RedirectHandler + + +/***/ }), + +/***/ 8861: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const RedirectHandler = __nccwpck_require__(2860) + +function createRedirectInterceptor ({ maxRedirections: defaultMaxRedirections }) { + return (dispatch) => { + return function Intercept (opts, handler) { + const { maxRedirections = defaultMaxRedirections } = opts + + if (!maxRedirections) { + return dispatch(opts, handler) + } + + const redirectHandler = new RedirectHandler(dispatch, maxRedirections, opts, handler) + opts = { ...opts, maxRedirections: 0 } // Stop sub dispatcher from also redirecting. + return dispatch(opts, redirectHandler) + } + } +} + +module.exports = createRedirectInterceptor + + +/***/ }), + +/***/ 953: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.SPECIAL_HEADERS = exports.HEADER_STATE = exports.MINOR = exports.MAJOR = exports.CONNECTION_TOKEN_CHARS = exports.HEADER_CHARS = exports.TOKEN = exports.STRICT_TOKEN = exports.HEX = exports.URL_CHAR = exports.STRICT_URL_CHAR = exports.USERINFO_CHARS = exports.MARK = exports.ALPHANUM = exports.NUM = exports.HEX_MAP = exports.NUM_MAP = exports.ALPHA = exports.FINISH = exports.H_METHOD_MAP = exports.METHOD_MAP = exports.METHODS_RTSP = exports.METHODS_ICE = exports.METHODS_HTTP = exports.METHODS = exports.LENIENT_FLAGS = exports.FLAGS = exports.TYPE = exports.ERROR = void 0; +const utils_1 = __nccwpck_require__(1891); +// C headers +var ERROR; +(function (ERROR) { + ERROR[ERROR["OK"] = 0] = "OK"; + ERROR[ERROR["INTERNAL"] = 1] = "INTERNAL"; + ERROR[ERROR["STRICT"] = 2] = "STRICT"; + ERROR[ERROR["LF_EXPECTED"] = 3] = "LF_EXPECTED"; + ERROR[ERROR["UNEXPECTED_CONTENT_LENGTH"] = 4] = "UNEXPECTED_CONTENT_LENGTH"; + ERROR[ERROR["CLOSED_CONNECTION"] = 5] = "CLOSED_CONNECTION"; + ERROR[ERROR["INVALID_METHOD"] = 6] = "INVALID_METHOD"; + ERROR[ERROR["INVALID_URL"] = 7] = "INVALID_URL"; + ERROR[ERROR["INVALID_CONSTANT"] = 8] = "INVALID_CONSTANT"; + ERROR[ERROR["INVALID_VERSION"] = 9] = "INVALID_VERSION"; + ERROR[ERROR["INVALID_HEADER_TOKEN"] = 10] = "INVALID_HEADER_TOKEN"; + ERROR[ERROR["INVALID_CONTENT_LENGTH"] = 11] = "INVALID_CONTENT_LENGTH"; + ERROR[ERROR["INVALID_CHUNK_SIZE"] = 12] = "INVALID_CHUNK_SIZE"; + ERROR[ERROR["INVALID_STATUS"] = 13] = "INVALID_STATUS"; + ERROR[ERROR["INVALID_EOF_STATE"] = 14] = "INVALID_EOF_STATE"; + ERROR[ERROR["INVALID_TRANSFER_ENCODING"] = 15] = "INVALID_TRANSFER_ENCODING"; + ERROR[ERROR["CB_MESSAGE_BEGIN"] = 16] = "CB_MESSAGE_BEGIN"; + ERROR[ERROR["CB_HEADERS_COMPLETE"] = 17] = "CB_HEADERS_COMPLETE"; + ERROR[ERROR["CB_MESSAGE_COMPLETE"] = 18] = "CB_MESSAGE_COMPLETE"; + ERROR[ERROR["CB_CHUNK_HEADER"] = 19] = "CB_CHUNK_HEADER"; + ERROR[ERROR["CB_CHUNK_COMPLETE"] = 20] = "CB_CHUNK_COMPLETE"; + ERROR[ERROR["PAUSED"] = 21] = "PAUSED"; + ERROR[ERROR["PAUSED_UPGRADE"] = 22] = "PAUSED_UPGRADE"; + ERROR[ERROR["PAUSED_H2_UPGRADE"] = 23] = "PAUSED_H2_UPGRADE"; + ERROR[ERROR["USER"] = 24] = "USER"; +})(ERROR = exports.ERROR || (exports.ERROR = {})); +var TYPE; +(function (TYPE) { + TYPE[TYPE["BOTH"] = 0] = "BOTH"; + TYPE[TYPE["REQUEST"] = 1] = "REQUEST"; + TYPE[TYPE["RESPONSE"] = 2] = "RESPONSE"; +})(TYPE = exports.TYPE || (exports.TYPE = {})); +var FLAGS; +(function (FLAGS) { + FLAGS[FLAGS["CONNECTION_KEEP_ALIVE"] = 1] = "CONNECTION_KEEP_ALIVE"; + FLAGS[FLAGS["CONNECTION_CLOSE"] = 2] = "CONNECTION_CLOSE"; + FLAGS[FLAGS["CONNECTION_UPGRADE"] = 4] = "CONNECTION_UPGRADE"; + FLAGS[FLAGS["CHUNKED"] = 8] = "CHUNKED"; + FLAGS[FLAGS["UPGRADE"] = 16] = "UPGRADE"; + FLAGS[FLAGS["CONTENT_LENGTH"] = 32] = "CONTENT_LENGTH"; + FLAGS[FLAGS["SKIPBODY"] = 64] = "SKIPBODY"; + FLAGS[FLAGS["TRAILING"] = 128] = "TRAILING"; + // 1 << 8 is unused + FLAGS[FLAGS["TRANSFER_ENCODING"] = 512] = "TRANSFER_ENCODING"; +})(FLAGS = exports.FLAGS || (exports.FLAGS = {})); +var LENIENT_FLAGS; +(function (LENIENT_FLAGS) { + LENIENT_FLAGS[LENIENT_FLAGS["HEADERS"] = 1] = "HEADERS"; + LENIENT_FLAGS[LENIENT_FLAGS["CHUNKED_LENGTH"] = 2] = "CHUNKED_LENGTH"; + LENIENT_FLAGS[LENIENT_FLAGS["KEEP_ALIVE"] = 4] = "KEEP_ALIVE"; +})(LENIENT_FLAGS = exports.LENIENT_FLAGS || (exports.LENIENT_FLAGS = {})); +var METHODS; +(function (METHODS) { + METHODS[METHODS["DELETE"] = 0] = "DELETE"; + METHODS[METHODS["GET"] = 1] = "GET"; + METHODS[METHODS["HEAD"] = 2] = "HEAD"; + METHODS[METHODS["POST"] = 3] = "POST"; + METHODS[METHODS["PUT"] = 4] = "PUT"; + /* pathological */ + METHODS[METHODS["CONNECT"] = 5] = "CONNECT"; + METHODS[METHODS["OPTIONS"] = 6] = "OPTIONS"; + METHODS[METHODS["TRACE"] = 7] = "TRACE"; + /* WebDAV */ + METHODS[METHODS["COPY"] = 8] = "COPY"; + METHODS[METHODS["LOCK"] = 9] = "LOCK"; + METHODS[METHODS["MKCOL"] = 10] = "MKCOL"; + METHODS[METHODS["MOVE"] = 11] = "MOVE"; + METHODS[METHODS["PROPFIND"] = 12] = "PROPFIND"; + METHODS[METHODS["PROPPATCH"] = 13] = "PROPPATCH"; + METHODS[METHODS["SEARCH"] = 14] = "SEARCH"; + METHODS[METHODS["UNLOCK"] = 15] = "UNLOCK"; + METHODS[METHODS["BIND"] = 16] = "BIND"; + METHODS[METHODS["REBIND"] = 17] = "REBIND"; + METHODS[METHODS["UNBIND"] = 18] = "UNBIND"; + METHODS[METHODS["ACL"] = 19] = "ACL"; + /* subversion */ + METHODS[METHODS["REPORT"] = 20] = "REPORT"; + METHODS[METHODS["MKACTIVITY"] = 21] = "MKACTIVITY"; + METHODS[METHODS["CHECKOUT"] = 22] = "CHECKOUT"; + METHODS[METHODS["MERGE"] = 23] = "MERGE"; + /* upnp */ + METHODS[METHODS["M-SEARCH"] = 24] = "M-SEARCH"; + METHODS[METHODS["NOTIFY"] = 25] = "NOTIFY"; + METHODS[METHODS["SUBSCRIBE"] = 26] = "SUBSCRIBE"; + METHODS[METHODS["UNSUBSCRIBE"] = 27] = "UNSUBSCRIBE"; + /* RFC-5789 */ + METHODS[METHODS["PATCH"] = 28] = "PATCH"; + METHODS[METHODS["PURGE"] = 29] = "PURGE"; + /* CalDAV */ + METHODS[METHODS["MKCALENDAR"] = 30] = "MKCALENDAR"; + /* RFC-2068, section 19.6.1.2 */ + METHODS[METHODS["LINK"] = 31] = "LINK"; + METHODS[METHODS["UNLINK"] = 32] = "UNLINK"; + /* icecast */ + METHODS[METHODS["SOURCE"] = 33] = "SOURCE"; + /* RFC-7540, section 11.6 */ + METHODS[METHODS["PRI"] = 34] = "PRI"; + /* RFC-2326 RTSP */ + METHODS[METHODS["DESCRIBE"] = 35] = "DESCRIBE"; + METHODS[METHODS["ANNOUNCE"] = 36] = "ANNOUNCE"; + METHODS[METHODS["SETUP"] = 37] = "SETUP"; + METHODS[METHODS["PLAY"] = 38] = "PLAY"; + METHODS[METHODS["PAUSE"] = 39] = "PAUSE"; + METHODS[METHODS["TEARDOWN"] = 40] = "TEARDOWN"; + METHODS[METHODS["GET_PARAMETER"] = 41] = "GET_PARAMETER"; + METHODS[METHODS["SET_PARAMETER"] = 42] = "SET_PARAMETER"; + METHODS[METHODS["REDIRECT"] = 43] = "REDIRECT"; + METHODS[METHODS["RECORD"] = 44] = "RECORD"; + /* RAOP */ + METHODS[METHODS["FLUSH"] = 45] = "FLUSH"; +})(METHODS = exports.METHODS || (exports.METHODS = {})); +exports.METHODS_HTTP = [ + METHODS.DELETE, + METHODS.GET, + METHODS.HEAD, + METHODS.POST, + METHODS.PUT, + METHODS.CONNECT, + METHODS.OPTIONS, + METHODS.TRACE, + METHODS.COPY, + METHODS.LOCK, + METHODS.MKCOL, + METHODS.MOVE, + METHODS.PROPFIND, + METHODS.PROPPATCH, + METHODS.SEARCH, + METHODS.UNLOCK, + METHODS.BIND, + METHODS.REBIND, + METHODS.UNBIND, + METHODS.ACL, + METHODS.REPORT, + METHODS.MKACTIVITY, + METHODS.CHECKOUT, + METHODS.MERGE, + METHODS['M-SEARCH'], + METHODS.NOTIFY, + METHODS.SUBSCRIBE, + METHODS.UNSUBSCRIBE, + METHODS.PATCH, + METHODS.PURGE, + METHODS.MKCALENDAR, + METHODS.LINK, + METHODS.UNLINK, + METHODS.PRI, + // TODO(indutny): should we allow it with HTTP? + METHODS.SOURCE, +]; +exports.METHODS_ICE = [ + METHODS.SOURCE, +]; +exports.METHODS_RTSP = [ + METHODS.OPTIONS, + METHODS.DESCRIBE, + METHODS.ANNOUNCE, + METHODS.SETUP, + METHODS.PLAY, + METHODS.PAUSE, + METHODS.TEARDOWN, + METHODS.GET_PARAMETER, + METHODS.SET_PARAMETER, + METHODS.REDIRECT, + METHODS.RECORD, + METHODS.FLUSH, + // For AirPlay + METHODS.GET, + METHODS.POST, +]; +exports.METHOD_MAP = utils_1.enumToMap(METHODS); +exports.H_METHOD_MAP = {}; +Object.keys(exports.METHOD_MAP).forEach((key) => { + if (/^H/.test(key)) { + exports.H_METHOD_MAP[key] = exports.METHOD_MAP[key]; + } +}); +var FINISH; +(function (FINISH) { + FINISH[FINISH["SAFE"] = 0] = "SAFE"; + FINISH[FINISH["SAFE_WITH_CB"] = 1] = "SAFE_WITH_CB"; + FINISH[FINISH["UNSAFE"] = 2] = "UNSAFE"; +})(FINISH = exports.FINISH || (exports.FINISH = {})); +exports.ALPHA = []; +for (let i = 'A'.charCodeAt(0); i <= 'Z'.charCodeAt(0); i++) { + // Upper case + exports.ALPHA.push(String.fromCharCode(i)); + // Lower case + exports.ALPHA.push(String.fromCharCode(i + 0x20)); +} +exports.NUM_MAP = { + 0: 0, 1: 1, 2: 2, 3: 3, 4: 4, + 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, +}; +exports.HEX_MAP = { + 0: 0, 1: 1, 2: 2, 3: 3, 4: 4, + 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, + A: 0XA, B: 0XB, C: 0XC, D: 0XD, E: 0XE, F: 0XF, + a: 0xa, b: 0xb, c: 0xc, d: 0xd, e: 0xe, f: 0xf, +}; +exports.NUM = [ + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', +]; +exports.ALPHANUM = exports.ALPHA.concat(exports.NUM); +exports.MARK = ['-', '_', '.', '!', '~', '*', '\'', '(', ')']; +exports.USERINFO_CHARS = exports.ALPHANUM + .concat(exports.MARK) + .concat(['%', ';', ':', '&', '=', '+', '$', ',']); +// TODO(indutny): use RFC +exports.STRICT_URL_CHAR = [ + '!', '"', '$', '%', '&', '\'', + '(', ')', '*', '+', ',', '-', '.', '/', + ':', ';', '<', '=', '>', + '@', '[', '\\', ']', '^', '_', + '`', + '{', '|', '}', '~', +].concat(exports.ALPHANUM); +exports.URL_CHAR = exports.STRICT_URL_CHAR + .concat(['\t', '\f']); +// All characters with 0x80 bit set to 1 +for (let i = 0x80; i <= 0xff; i++) { + exports.URL_CHAR.push(i); +} +exports.HEX = exports.NUM.concat(['a', 'b', 'c', 'd', 'e', 'f', 'A', 'B', 'C', 'D', 'E', 'F']); +/* Tokens as defined by rfc 2616. Also lowercases them. + * token = 1* + * separators = "(" | ")" | "<" | ">" | "@" + * | "," | ";" | ":" | "\" | <"> + * | "/" | "[" | "]" | "?" | "=" + * | "{" | "}" | SP | HT + */ +exports.STRICT_TOKEN = [ + '!', '#', '$', '%', '&', '\'', + '*', '+', '-', '.', + '^', '_', '`', + '|', '~', +].concat(exports.ALPHANUM); +exports.TOKEN = exports.STRICT_TOKEN.concat([' ']); +/* + * Verify that a char is a valid visible (printable) US-ASCII + * character or %x80-FF + */ +exports.HEADER_CHARS = ['\t']; +for (let i = 32; i <= 255; i++) { + if (i !== 127) { + exports.HEADER_CHARS.push(i); + } +} +// ',' = \x44 +exports.CONNECTION_TOKEN_CHARS = exports.HEADER_CHARS.filter((c) => c !== 44); +exports.MAJOR = exports.NUM_MAP; +exports.MINOR = exports.MAJOR; +var HEADER_STATE; +(function (HEADER_STATE) { + HEADER_STATE[HEADER_STATE["GENERAL"] = 0] = "GENERAL"; + HEADER_STATE[HEADER_STATE["CONNECTION"] = 1] = "CONNECTION"; + HEADER_STATE[HEADER_STATE["CONTENT_LENGTH"] = 2] = "CONTENT_LENGTH"; + HEADER_STATE[HEADER_STATE["TRANSFER_ENCODING"] = 3] = "TRANSFER_ENCODING"; + HEADER_STATE[HEADER_STATE["UPGRADE"] = 4] = "UPGRADE"; + HEADER_STATE[HEADER_STATE["CONNECTION_KEEP_ALIVE"] = 5] = "CONNECTION_KEEP_ALIVE"; + HEADER_STATE[HEADER_STATE["CONNECTION_CLOSE"] = 6] = "CONNECTION_CLOSE"; + HEADER_STATE[HEADER_STATE["CONNECTION_UPGRADE"] = 7] = "CONNECTION_UPGRADE"; + HEADER_STATE[HEADER_STATE["TRANSFER_ENCODING_CHUNKED"] = 8] = "TRANSFER_ENCODING_CHUNKED"; +})(HEADER_STATE = exports.HEADER_STATE || (exports.HEADER_STATE = {})); +exports.SPECIAL_HEADERS = { + 'connection': HEADER_STATE.CONNECTION, + 'content-length': HEADER_STATE.CONTENT_LENGTH, + 'proxy-connection': HEADER_STATE.CONNECTION, + 'transfer-encoding': HEADER_STATE.TRANSFER_ENCODING, + 'upgrade': HEADER_STATE.UPGRADE, +}; +//# sourceMappingURL=constants.js.map + +/***/ }), + +/***/ 1145: +/***/ ((module) => { + +module.exports = 'AGFzbQEAAAABMAhgAX8Bf2ADf39/AX9gBH9/f38Bf2AAAGADf39/AGABfwBgAn9/AGAGf39/f39/AALLAQgDZW52GHdhc21fb25faGVhZGVyc19jb21wbGV0ZQACA2VudhV3YXNtX29uX21lc3NhZ2VfYmVnaW4AAANlbnYLd2FzbV9vbl91cmwAAQNlbnYOd2FzbV9vbl9zdGF0dXMAAQNlbnYUd2FzbV9vbl9oZWFkZXJfZmllbGQAAQNlbnYUd2FzbV9vbl9oZWFkZXJfdmFsdWUAAQNlbnYMd2FzbV9vbl9ib2R5AAEDZW52GHdhc21fb25fbWVzc2FnZV9jb21wbGV0ZQAAA0ZFAwMEAAAFAAAAAAAABQEFAAUFBQAABgAAAAAGBgYGAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAAABAQcAAAUFAwABBAUBcAESEgUDAQACBggBfwFBgNQECwfRBSIGbWVtb3J5AgALX2luaXRpYWxpemUACRlfX2luZGlyZWN0X2Z1bmN0aW9uX3RhYmxlAQALbGxodHRwX2luaXQAChhsbGh0dHBfc2hvdWxkX2tlZXBfYWxpdmUAQQxsbGh0dHBfYWxsb2MADAZtYWxsb2MARgtsbGh0dHBfZnJlZQANBGZyZWUASA9sbGh0dHBfZ2V0X3R5cGUADhVsbGh0dHBfZ2V0X2h0dHBfbWFqb3IADxVsbGh0dHBfZ2V0X2h0dHBfbWlub3IAEBFsbGh0dHBfZ2V0X21ldGhvZAARFmxsaHR0cF9nZXRfc3RhdHVzX2NvZGUAEhJsbGh0dHBfZ2V0X3VwZ3JhZGUAEwxsbGh0dHBfcmVzZXQAFA5sbGh0dHBfZXhlY3V0ZQAVFGxsaHR0cF9zZXR0aW5nc19pbml0ABYNbGxodHRwX2ZpbmlzaAAXDGxsaHR0cF9wYXVzZQAYDWxsaHR0cF9yZXN1bWUAGRtsbGh0dHBfcmVzdW1lX2FmdGVyX3VwZ3JhZGUAGhBsbGh0dHBfZ2V0X2Vycm5vABsXbGxodHRwX2dldF9lcnJvcl9yZWFzb24AHBdsbGh0dHBfc2V0X2Vycm9yX3JlYXNvbgAdFGxsaHR0cF9nZXRfZXJyb3JfcG9zAB4RbGxodHRwX2Vycm5vX25hbWUAHxJsbGh0dHBfbWV0aG9kX25hbWUAIBJs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ADcDACAAQShqQgA3AwAgAEEgakIANwMAIABBEGpCADcDACAAQQhqQgA3AwAgAEHdATYCHEEAC3sBAX8CQCAAKAIMIgMNAAJAIAAoAgRFDQAgACABNgIECwJAIAAgASACEMSAgIAAIgMNACAAKAIMDwsgACADNgIcQQAhAyAAKAIEIgFFDQAgACABIAIgACgCCBGBgICAAAAiAUUNACAAIAI2AhQgACABNgIMIAEhAwsgAwvk8wEDDn8DfgR/I4CAgIAAQRBrIgMkgICAgAAgASEEIAEhBSABIQYgASEHIAEhCCABIQkgASEKIAEhCyABIQwgASENIAEhDiABIQ8CQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAgACgCHCIQQX9qDt0B2gEB2QECAwQFBgcICQoLDA0O2AEPENcBERLWARMUFRYXGBkaG+AB3wEcHR7VAR8gISIjJCXUASYnKCkqKyzTAdIBLS7RAdABLzAxMjM0NTY3ODk6Ozw9Pj9AQUJDREVG2wFHSElKzwHOAUvNAUzMAU1OT1BRUlNUVVZXWFlaW1xdXl9gYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp7fH1+f4ABgQGCAYMBhAGFAYYBhwGIAYkBigGLAYwBjQGOAY8BkAGRAZIBkwGUAZUBlgGXAZgBmQGaAZsBnAGdAZ4BnwGgAaEBogGjAaQBpQGmAacBqAGpAaoBqwGsAa0BrgGvAbABsQGyAbMBtAG1AbYBtwHLAcoBuAHJAbkByAG6AbsBvAG9Ab4BvwHAAcEBwgHDAcQBxQHGAQDcAQtBACEQDMYBC0EOIRAMxQELQQ0hEAzEAQtBDyEQDMMBC0EQIRAMwgELQRMhEAzBAQtBFCEQDMABC0EVIRAMvwELQRYhEAy+AQtBFyEQDL0BC0EYIRAMvAELQRkhEAy7AQtBGiEQDLoBC0EbIRAMuQELQRwhEAy4AQtBCCEQDLcBC0EdIRAMtgELQSAhEAy1AQtBHyEQDLQBC0EHIRAMswELQSEhEAyyAQtBIiEQDLEBC0EeIRAMsAELQSMhEAyvAQtBEiEQDK4BC0ERIRAMrQELQSQhEAysAQtBJSEQDKsBC0EmIRAMqgELQSchEAypAQtBwwEhEAyoAQtBKSEQDKcBC0ErIRAMpgELQSwhEAylAQtBLSEQDKQBC0EuIRAMowELQS8hEAyiAQtBxAEhEAyhAQtBMCEQDKABC0E0IRAMnwELQQwhEAyeAQtBMSEQDJ0BC0EyIRAMnAELQTMhEAybAQtBOSEQDJoBC0E1IRAMmQELQcUBIRAMmAELQQshEAyXAQtBOiEQDJYBC0E2IRAMlQELQQohEAyUAQtBNyEQDJMBC0E4IRAMkgELQTwhEAyRAQtBOyEQDJABC0E9IRAMjwELQQkhEAyOAQtBKCEQDI0BC0E+IRAMjAELQT8hEAyLAQtBwAAhEAyKAQtBwQAhEAyJAQtBwgAhEAyIAQtBwwAhEAyHAQtBxAAhEAyGAQtBxQAhEAyFAQtBxgAhEAyEAQtBKiEQDIMBC0HHACEQDIIBC0HIACEQDIEBC0HJACEQDIABC0HKACEQDH8LQcsAIRAMfgtBzQAhEAx9C0HMACEQDHwLQc4AIRAMewtBzwAhEAx6C0HQACEQDHkLQdEAIRAMeAtB0gAhEAx3C0HTACEQDHYLQdQAIRAMdQtB1gAhEAx0C0HVACEQDHMLQQYhEAxyC0HXACEQDHELQQUhEAxwC0HYACEQDG8LQQQhEAxuC0HZACEQDG0LQdoAIRAMbAtB2wAhEAxrC0HcACEQDGoLQQMhEAxpC0HdACEQDGgLQd4AIRAMZwtB3wAhEAxmC0HhACEQDGULQeAAIRAMZAtB4gAhEAxjC0HjACEQDGILQQIhEAxhC0HkACEQDGALQeUAIRAMXwtB5gAhEAxeC0HnACEQDF0LQegAIRAMXAtB6QAhEAxbC0HqACEQDFoLQesAIRAMWQtB7AAhEAxYC0HtACEQDFcLQe4AIRAMVgtB7wAhEAxVC0HwACEQDFQLQfEAIRAMUwtB8gAhEAxSC0HzACEQDFELQfQAIRAMUAtB9QAhEAxPC0H2ACEQDE4LQfcAIRAMTQtB+AAhEAxMC0H5ACEQDEsLQfoAIRAMSgtB+wAhEAxJC0H8ACEQDEgLQf0AIRAMRwtB/gAhEAxGC0H/ACEQDEULQYABIRAMRAtBgQEhEAxDC0GCASEQDEILQYMBIRAMQQtBhAEhEAxAC0GFASEQDD8LQYYBIRAMPgtBhwEhEAw9C0GIASEQDDwLQYkBIRAMOwtBigEhEAw6C0GLASEQDDkLQYwBIRAMOAtBjQEhEAw3C0GOASEQDDYLQY8BIRAMNQtBkAEhEAw0C0GRASEQDDMLQZIBIRAMMgtBkwEhEAwxC0GUASEQDDALQZUBIRAMLwtBlgEhEAwuC0GXASEQDC0LQZgBIRAMLAtBmQEhEAwrC0GaASEQDCoLQZsBIRAMKQtBnAEhEAwoC0GdASEQDCcLQZ4BIRAMJgtBnwEhEAwlC0GgASEQDCQLQaEBIRAMIwtBogEhEAwiC0GjASEQDCELQaQBIRAMIAtBpQEhEAwfC0GmASEQDB4LQacBIRAMHQtBqAEhEAwcC0GpASEQDBsLQaoBIRAMGgtBqwEhEAwZC0GsASEQDBgLQa0BIRAMFwtBrgEhEAwWC0EBIRAMFQtBrwEhEAwUC0GwASEQDBMLQbEBIRAMEgtBswEhEAwRC0GyASEQDBALQbQBIRAMDwtBtQEhEAwOC0G2ASEQDA0LQbcBIRAMDAtBuAEhEAwLC0G5ASEQDAoLQboBIRAMCQtBuwEhEAwIC0HGASEQDAcLQbwBIRAMBgtBvQEhEAwFC0G+ASEQDAQLQb8BIRAMAwtBwAEhEAwCC0HCASEQDAELQcEBIRALA0ACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkAC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FCAAIBA2AgxBACEQDLIBCyAAKAIEIRAgAEEANgIEAkAgACAQIAEQp4CAgAAiEA0AIAEhAQxNCyAAQdMANgIcIAAgATYCFCAAIBA2AgxBACEQDLEBCyAAKAIEIRAgAEEANgIEAkAgACAQIAEQp4CAgAAiEA0AIAEhAQxRCyAAQeUANgIcIAAgATYCFCAAIBA2AgxBACEQDLABCyAAQQA2AhwgACABNgIUIABBxoqAgAA2AhAgAEEHNgIMQQAhEAyvAQsgACgCBCEQIABBADYCBAJAIAAgECABEKeAgIAAIhANACABIQEMSQsgAEHSADYCHCAAIAE2AhQgACAQNgIMQQAhEAyuAQsgACgCBCEQIABBADYCBAJAIAAgECABEKeAgIAAIhANACABIQEMSQsgAEHTADYCHCAAIAE2AhQgACAQNgIMQQAhEAytAQsgACgCBCEQIABBADYCBAJAIAAgECABEKeAgIAAIhANACABIQEMTQsgAEHlADYCHCAAIAE2AhQgACAQNgIMQQAhEAysAQsgAEEANgIcIAAgATYCFCAAQdyIgIAANgIQIABBBzYCDEEAIRAMqwELIBBBP0cNASABQQFqIQELQQUhEAyQAQtBACEQIABBADYCHCAAIAE2AhQgAEH9koCAADYCECAAQQc2AgwMqAELIAAoAgQhECAAQQA2AgQCQCAAIBAgARCngICAACIQDQAgASEBDEILIABB0gA2AhwgACABNgIUIAAgEDYCDEEAIRAMpwELIAAoAgQhECAAQQA2AgQCQCAAIBAgARCngICAACIQDQAgASEBDEILIABB0wA2AhwgACABNgIUIAAgEDYCDEEAIRAMpgELIAAoAgQhECAAQQA2AgQCQCAAIBAgARCngICAACIQDQAgASEBDEYLIABB5QA2AhwgACABNgIUIAAgEDYCDEEAIRAMpQELIAAoAgQhASAAQQA2AgQCQCAAIAEgFBCngICAACIBDQAgFCEBDD8LIABB0gA2AhwgACAUNgIUIAAgATYCDEEAIRAMpAELIAAoAgQhASAAQQA2AgQCQCAAIAEgFBCngICAACIBDQAgFCEBDD8LIABB0wA2AhwgACAUNgIUIAAgATYCDEEAIRAMowELIAAoAgQhASAAQQA2AgQCQCAAIAEgFBCngICAACIBDQAgFCEBDEMLIABB5QA2AhwgACAU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YWdlX2JlZ2luYCBjYWxsYmFjayBlcnJvcgBgb25fY2h1bmtfZXh0ZW5zaW9uX3ZhbHVlYCBjYWxsYmFjayBlcnJvcgBgb25fc3RhdHVzX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fdmVyc2lvbl9jb21wbGV0ZWAgY2FsbGJhY2sgZXJyb3IAYG9uX3VybF9jb21wbGV0ZWAgY2FsbGJhY2sgZXJyb3IAYG9uX2NodW5rX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25faGVhZGVyX3ZhbHVlX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fbWVzc2FnZV9jb21wbGV0ZWAgY2FsbGJhY2sgZXJyb3IAYG9uX21ldGhvZF9jb21wbGV0ZWAgY2FsbGJhY2sgZXJyb3IAYG9uX2hlYWRlcl9maWVsZF9jb21wbGV0ZWAgY2FsbGJhY2sgZXJyb3IAYG9uX2NodW5rX2V4dGVuc2lvbl9uYW1lYCBjYWxsYmFjayBlcnJvcgBVbmV4cGVjdGVkIGNoYXIgaW4gdXJsIHNlcnZlcgBJbnZhbGlkIGhlYWRlciB2YWx1ZSBjaGFyAEludmFsaWQgaGVhZGVyIGZpZWxkIGNoYXIAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl92ZXJzaW9uAEludmFsaWQgbWlub3IgdmVyc2lvbgBJbnZhbGlkIG1ham9yIHZlcnNpb24ARXhwZWN0ZWQgc3BhY2UgYWZ0ZXIgdmVyc2lvbgBFeHBlY3RlZCBDUkxGIGFmdGVyIHZlcnNpb24ASW52YWxpZCBIVFRQIHZlcnNpb24ASW52YWxpZCBoZWFkZXIgdG9rZW4AU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl91cmwASW52YWxpZCBjaGFyYWN0ZXJzIGluIHVybABVbmV4cGVjdGVkIHN0YXJ0IGNoYXIgaW4gdXJsAERvdWJsZSBAIGluIHVybABFbXB0eSBDb250ZW50LUxlbmd0aABJbnZhbGlkIGNoYXJhY3RlciBpbiBDb250ZW50LUxlbmd0aABEdXBsaWNhdGUgQ29udGVudC1MZW5ndGgASW52YWxpZCBjaGFyIGluIHVybCBwYXRoAENvbnRlbnQtTGVuZ3RoIGNhbid0IGJlIHByZXNlbnQgd2l0aCBUcmFuc2Zlci1FbmNvZGluZwBJbnZhbGlkIGNoYXJhY3RlciBpbiBjaHVuayBzaXplAFNwYW4gY2FsbGJhY2sgZXJyb3IgaW4gb25faGVhZGVyX3ZhbHVlAFNwYW4gY2FsbGJhY2sgZXJyb3IgaW4gb25fY2h1bmtfZXh0ZW5zaW9uX3ZhbHVlAEludmFsaWQgY2hhcmFjdGVyIGluIGNodW5rIGV4dGVuc2lvbnMgdmFsdWUATWlzc2luZyBleHBlY3RlZCBMRiBhZnRlciBoZWFkZXIgdmFsdWUASW52YWxpZCBgVHJhbnNmZXItRW5jb2RpbmdgIGhlYWRlciB2YWx1ZQBJbnZhbGlkIGNoYXJhY3RlciBpbiBjaHVuayBleHRlbnNpb25zIHF1b3RlIHZhbHVlAEludmFsaWQgY2hhcmFjdGVyIGluIGNodW5rIGV4dGVuc2lvbnMgcXVvdGVkIHZhbHVlAFBhdXNlZCBieSBvbl9oZWFkZXJzX2NvbXBsZXRlAEludmFsaWQgRU9GIHN0YXRlAG9uX3Jlc2V0IHBhdXNlAG9uX2NodW5rX2hlYWRlciBwYXVzZQBvbl9tZXNzYWdlX2JlZ2luIHBhdXNlAG9uX2NodW5rX2V4dGVuc2lvbl92YWx1ZSBwYXVzZQBvbl9zdGF0dXNfY29tcGxldGUgcGF1c2UAb25fdmVyc2lvbl9jb21wbGV0ZSBwYXVzZQBvbl91cmxfY29tcGxldGUgcGF1c2UAb25fY2h1bmtfY29tcGxldGUgcGF1c2UAb25faGVhZGVyX3ZhbHVlX2NvbXBsZXRlIHBhdXNlAG9uX21lc3NhZ2VfY29tcGxldGUgcGF1c2UAb25fbWV0aG9kX2NvbXBsZXRlIHBhdXNlAG9uX2hlYWRlcl9maWVsZF9jb21wbGV0ZSBwYXVzZQBvbl9jaHVua19leHRlbnNpb25fbmFtZSBwYXVzZQBVbmV4cGVjdGVkIHNwYWNlIGFmdGVyIHN0YXJ0IGxpbmUAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9jaHVua19leHRlbnNpb25fbmFtZQBJbnZhbGlkIGNoYXJhY3RlciBpbiBjaHVuayBleHRlbnNpb25zIG5hbWUAUGF1c2Ugb24gQ09OTkVDVC9VcGdyYWRlAFBhdXNlIG9uIFBSSS9VcGdyYWRlAEV4cGVjdGVkIEhUVFAvMiBDb25uZWN0aW9uIFByZWZhY2UAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9tZXRob2QARXhwZWN0ZWQgc3BhY2UgYWZ0ZXIgbWV0aG9kAFNwYW4gY2FsbGJhY2sgZXJyb3IgaW4gb25faGVhZGVyX2ZpZWxkAFBhdXNlZABJbnZhbGlkIHdvcmQgZW5jb3VudGVyZWQASW52YWxpZCBtZXRob2QgZW5jb3VudGVyZWQAVW5leHBlY3RlZCBjaGFyIGluIHVybCBzY2hlbWEAUmVxdWVzdCBoYXMgaW52YWxpZCBgVHJhbnNmZXItRW5jb2RpbmdgAFNXSVRDSF9QUk9YWQBVU0VfUFJPWFkATUtBQ1RJVklUWQBVTlBST0NFU1NBQkxFX0VOVElUWQBDT1BZAE1PVkVEX1BFUk1BTkVOVExZAFRPT19FQVJMWQBOT1RJRlkARkFJTEVEX0RFUEVOREVOQ1kAQkFEX0dBVEVXQVkAUExBWQBQVVQAQ0hFQ0tPVVQAR0FURVdBWV9USU1FT1VUAFJFUVVFU1RfVElNRU9VVABORVRXT1JLX0NPTk5FQ1RfVElNRU9VVABDT05ORUNUSU9OX1RJTUVPVVQATE9HSU5fVElNRU9VVABORVRXT1JLX1JFQURfVElNRU9VVABQT1NUAE1JU0RJUkVDVEVEX1JFUVVFU1QAQ0xJRU5UX0NMT1NFRF9SRVFVRVNUAENMSUVOVF9DTE9TRURfTE9BRF9CQUxBTkNFRF9SRVFVRVNUAEJBRF9SRVFVRVNUAEhUVFBfUkVRVUVTVF9TRU5UX1RPX0hUVFBTX1BPUlQAUkVQT1JUAElNX0FfVEVBUE9UAFJFU0VUX0NPTlRFTlQATk9fQ09OVEVOVABQQVJUSUFMX0NPTlRFTlQASFBFX0lOVkFMSURfQ09OU1RBTlQASFBFX0NCX1JFU0VUAEdFVABIUEVfU1RSSUNUAENPTkZMSUNUAFRFTVBPUkFSWV9SRURJUkVDVABQRVJNQU5FTlRfUkVESVJFQ1QAQ09OTkVDVABNVUxUSV9TVEFUVVMASFBFX0lOVkFMSURfU1RBVFVTAFRPT19NQU5ZX1JFUVVFU1RTAEVBUkxZX0hJTlRTAFVOQVZBSUxBQkxFX0ZPUl9MRUdBTF9SRUFTT05TAE9QVElPTlMAU1dJVENISU5HX1BST1RPQ09MUwBWQVJJQU5UX0FMU09fTkVHT1RJQVRFUwBNVUxUSVBMRV9DSE9JQ0VTAElOVEVSTkFMX1NFUlZFUl9FUlJPUgBXRUJfU0VSVkVSX1VOS05PV05fRVJST1IAUkFJTEdVTl9FUlJPUgBJREVOVElUWV9QUk9WSURFUl9BVVRIRU5USUNBVElPTl9FUlJPUgBTU0xfQ0VSVElGSUNBVEVfRVJST1IASU5WQUxJRF9YX0ZPUldBUkRFRF9GT1IAU0VUX1BBUkFNRVRFUgBHRVRfUEFSQU1FVEVSAEhQRV9VU0VSAFNFRV9PVEhFUgBIUEVfQ0JfQ0hVTktfSEVBREVSAE1LQ0FMRU5EQVIAU0VUVVAAV0VCX1NFUlZFUl9JU19ET1dOAFRFQVJET1dOAEhQRV9DTE9TRURfQ09OTkVDVElPTgBIRVVSSVNUSUNfRVhQSVJBVElPTgBESVNDT05ORUNURURfT1BFUkFUSU9OAE5PTl9BVVRIT1JJVEFUSVZFX0lORk9STUFUSU9OAEhQRV9JTlZBTElEX1ZFUlNJT04ASFBFX0NCX01FU1NBR0VfQkVHSU4AU0lURV9JU19GUk9aRU4ASFBFX0lOVkFMSURfSEVBREVSX1RPS0VOAElOVkFMSURfVE9LRU4ARk9SQklEREVOAEVOSEFOQ0VfWU9VUl9DQUxNAEhQRV9JTlZBTElEX1VSTABCTE9DS0VEX0JZX1BBUkVOVEFMX0NPTlRST0wATUtDT0wAQUNMAEhQRV9JTlRFUk5BTABSRVFVRVNUX0hFQURFUl9GSUVMRFNfVE9PX0xBUkdFX1VOT0ZGSUNJQUwASFBFX09LAFVOTElOSwBVTkxPQ0sAUFJJAFJFVFJZX1dJVEgASFBFX0lOVkFMSURfQ09OVEVOVF9MRU5HVEgASFBFX1VORVhQRUNURURfQ09OVEVOVF9MRU5HVEgARkxVU0gAUFJPUFBBVENIAE0tU0VBUkNIAFVSSV9UT09fTE9ORwBQUk9DRVNTSU5HAE1JU0NFTExBTkVPVVNfUEVSU0lTVEVOVF9XQVJOSU5HAE1JU0NFTExBTkVPVVNfV0FSTklORwBIUEVfSU5WQUxJRF9UUkFOU0ZFUl9FTkNPRElORwBFeHBlY3RlZCBDUkxGAEhQRV9JTlZBTElEX0NIVU5LX1NJWkUATU9WRQBDT05USU5VRQBIUEVfQ0JfU1RBVFVTX0NPTVBMRVRFAEhQRV9DQl9IRUFERVJTX0NPTVBMRVRFAEhQRV9DQl9WRVJTSU9OX0NPTVBMRVRFAEhQRV9DQl9VUkxfQ09NUExFVEUASFBFX0NCX0NIVU5LX0NPTVBMRVRFAEhQRV9DQl9IRUFERVJfVkFMVUVfQ09NUExFVEUASFBFX0NCX0NIVU5LX0VYVEVOU0lPTl9WQUxVRV9DT01QTEVURQBIUEVfQ0JfQ0hVTktfRVhURU5TSU9OX05BTUVfQ09NUExFVEUASFBFX0NCX01FU1NBR0VfQ09NUExFVEUASFBFX0NCX01FVEhPRF9DT01QTEVURQBIUEVfQ0JfSEVBREVSX0ZJRUxEX0NPTVBMRVRFAERFTEVURQBIUEVfSU5WQUxJRF9FT0ZfU1RBVEUASU5WQUxJRF9TU0xfQ0VSVElGSUNBVEUAUEFVU0UATk9fUkVTUE9OU0UAVU5TVVBQT1JURURfTUVESUFfVFlQRQBHT05FAE5PVF9BQ0NFUFRBQkxFAFNFUlZJQ0VfVU5BVkFJTEFCTEUAUkFOR0VfTk9UX1NBVElTRklBQkxFAE9SSUdJTl9JU19VTlJFQUNIQUJMRQBSRVNQT05TRV9JU19TVEFMRQBQVVJHRQBNRVJHRQBSRVFVRVNUX0hFQURFUl9GSUVMRFNfVE9PX0xBUkdFAFJFUVVFU1RfSEVBREVSX1RPT19MQVJHRQBQQVlMT0FEX1RPT19MQVJHRQBJTlNVRkZJQ0lFTlRfU1RPUkFHRQBIUEVfUEFVU0VEX1VQR1JBREUASFBFX1BBVVNFRF9IMl9VUEdSQURFAFNPVVJDRQBBTk5PVU5DRQBUUkFDRQBIUEVfVU5FWFBFQ1RFRF9TUEFDRQBERVNDUklCRQBVTlNVQlNDUklCRQBSRUNPUkQASFBFX0lOVkFMSURfTUVUSE9EAE5PVF9GT1VORABQUk9QRklORABVTkJJTkQAUkVCSU5EAFVOQVVUSE9SSVpFRABNRVRIT0RfTk9UX0FMTE9XRUQASFRUUF9WRVJTSU9OX05PVF9TVVBQT1JURUQAQUxSRUFEWV9SRVBPUlRFRABBQ0NFUFRFRABOT1RfSU1QTEVNRU5URUQATE9PUF9ERVRFQ1RFRABIUEVfQ1JfRVhQRUNURUQASFBFX0xGX0VYUEVDVEVEAENSRUFURUQASU1fVVNFRABIUEVfUEFVU0VEAFRJTUVPVVRfT0NDVVJFRABQQVlNRU5UX1JFUVVJUkVEAFBSRUNPTkRJVElPTl9SRVFVSVJFRABQUk9YWV9BVVRIRU5USUNBVElPTl9SRVFVSVJFRABORVRXT1JLX0FVVEhFTlRJQ0FUSU9OX1JFUVVJUkVEAExFTkdUSF9SRVFVSVJFRABTU0xfQ0VSVElGSUNBVEVfUkVRVUlSRUQAVVBHUkFERV9SRVFVSVJFRABQQUdFX0VYUElSRUQAUFJFQ09ORElUSU9OX0ZBSUxFRABFWFBFQ1RBVElPTl9GQUlMRUQAUkVWQUxJREFUSU9OX0ZBSUxFRABTU0xfSEFORFNIQUtFX0ZBSUxFRABMT0NLRUQAVFJBTlNGT1JNQVRJT05fQVBQTElFRABOT1RfTU9ESUZJRUQATk9UX0VYVEVOREVEAEJBTkRXSURUSF9MSU1JVF9FWENFRURFRABTSVRFX0lTX09WRVJMT0FERUQASEVBRABFeHBlY3RlZCBIVFRQLwAAXhMAACYTAAAwEAAA8BcAAJ0TAAAVEgAAORcAAPASAAAKEAAAdRIAAK0SAACCEwAATxQAAH8QAACgFQAAIxQAAIkSAACLFAAATRUAANQRAADPFAAAEBgAAMkWAADcFgAAwREAAOAXAAC7FAAAdBQAAHwVAADlFAAACBcAAB8QAABlFQAAoxQAACgVAAACFQAAmRUAACwQAACLGQAATw8AANQOAABqEAAAzhAAAAIXAACJDgAAbhMAABwTAABmFAAAVhcAAMETAADNEwAAbBMAAGgXAABmFwAAXxcAACITAADODwAAaQ4AANgOAABjFgAAyxMAAKoOAAAoFwAAJhcAAMUTAABdFgAA6BEAAGcTAABlEwAA8hYAAHMTAAAdFwAA+RYAAPMRAADPDgAAzhUAAAwSAACzEQAApREAAGEQAAAyFwAAuxMAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQIBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIDAgICAgIAAAICAAICAAICAgICAgICAgIABAAAAAAAAgICAgICAgICAgICAgICAgICAgICAgICAgIAAAACAgICAgICAgICAgICAgICAgICAgICAgICAgICAgACAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAACAAICAgICAAACAgACAgACAgICAgICAgICAAMABAAAAAICAgICAgICAgICAgICAgICAgICAgICAgICAAAAAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAAgACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAbG9zZWVlcC1hbGl2ZQAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAQEBAQEBAQEBAQIBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBY2h1bmtlZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEAAQEBAQEAAAEBAAEBAAEBAQEBAQEBAQEAAAAAAAAAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABlY3Rpb25lbnQtbGVuZ3Rob25yb3h5LWNvbm5lY3Rpb24AAAAAAAAAAAAAAAAAAAByYW5zZmVyLWVuY29kaW5ncGdyYWRlDQoNCg0KU00NCg0KVFRQL0NFL1RTUC8AAAAAAAAAAAAAAAABAgABAwAAAAAAAAAAAAAAAAAAAAAAAAQBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAQIAAQMAAAAAAAAAAAAAAAAAAAAAAAAEAQEFAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAEAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAAAAQAAAgAAAAAAAAAAAAAAAAAAAAAAAAMEAAAEBAQEBAQEBAQEBAUEBAQEBAQEBAQEBAQABAAGBwQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEAAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAEAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAADAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwAAAAAAAAMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAABAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAIAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAAAAAAAADAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABOT1VOQ0VFQ0tPVVRORUNURVRFQ1JJQkVMVVNIRVRFQURTRUFSQ0hSR0VDVElWSVRZTEVOREFSVkVPVElGWVBUSU9OU0NIU0VBWVNUQVRDSEdFT1JESVJFQ1RPUlRSQ0hQQVJBTUVURVJVUkNFQlNDUklCRUFSRE9XTkFDRUlORE5LQ0tVQlNDUklCRUhUVFAvQURUUC8=' + + +/***/ }), + +/***/ 5627: +/***/ ((module) => { + +module.exports = 'AGFzbQEAAAABMAhgAX8Bf2ADf39/AX9gBH9/f38Bf2AAAGADf39/AGABfwBgAn9/AGAGf39/f39/AALLAQgDZW52GHdhc21fb25faGVhZGVyc19jb21wbGV0ZQACA2VudhV3YXNtX29uX21lc3NhZ2VfYmVnaW4AAANlbnYLd2FzbV9vbl91cmwAAQNlbnYOd2FzbV9vbl9zdGF0dXMAAQNlbnYUd2FzbV9vbl9oZWFkZXJfZmllbGQAAQNlbnYUd2FzbV9vbl9oZWFkZXJfdmFsdWUAAQNlbnYMd2FzbV9vbl9ib2R5AAEDZW52GHdhc21fb25fbWVzc2FnZV9jb21wbGV0ZQAAA0ZFAwMEAAAFAAAAAAAABQEFAAUFBQAABgAAAAAGBgYGAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAAABAQcAAAUFAwABBAUBcAESEgUDAQACBggBfwFBgNQECwfRBSIGbWVtb3J5AgALX2luaXRpYWxpemUACRlfX2luZGlyZWN0X2Z1bmN0aW9uX3RhYmxlAQALbGxodHRwX2luaXQAChhsbGh0dHBfc2hvdWxkX2tlZXBfYWxpdmUAQQxsbGh0dHBfYWxsb2MADAZtYWxsb2MARgtsbGh0dHBfZnJlZQANBGZyZWUASA9sbGh0dHBfZ2V0X3R5cGUADhVsbGh0dHBfZ2V0X2h0dHBfbWFqb3IADxVsbGh0dHBfZ2V0X2h0dHBfbWlub3IAEBFsbGh0dHBfZ2V0X21ldGhvZAARFmxsaHR0cF9nZXRfc3RhdHVzX2NvZGUAEhJsbGh0dHBfZ2V0X3VwZ3JhZGUAEwxsbGh0dHBfcmVzZXQAFA5sbGh0dHBfZXhlY3V0ZQAVFGxsaHR0cF9zZXR0aW5nc19pbml0ABYNbGxodHRwX2ZpbmlzaAAXDGxsaHR0cF9wYXVzZQAYDWxsaHR0cF9yZXN1bWUAGRtsbGh0dHBfcmVzdW1lX2FmdGVyX3VwZ3JhZGUAGhBsbGh0dHBfZ2V0X2Vycm5vABsXbGxodHRwX2dldF9lcnJvcl9yZWFzb24AHBdsbGh0dHBfc2V0X2Vycm9yX3JlYXNvbgAdFGxsaHR0cF9nZXRfZXJyb3JfcG9zAB4RbGxodHRwX2Vycm5vX25hbWUAHxJsbGh0dHBfbWV0aG9kX25hbWUAIBJs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IAH9CwMAIABBMGogAf0LAwAgAEEgaiAB/QsDACAAQd0BNgIcQQALewEBfwJAIAAoAgwiAw0AAkAgACgCBEUNACAAIAE2AgQLAkAgACABIAIQxICAgAAiAw0AIAAoAgwPCyAAIAM2AhxBACEDIAAoAgQiAUUNACAAIAEgAiAAKAIIEYGAgIAAACIBRQ0AIAAgAjYCFCAAIAE2AgwgASEDCyADC+TzAQMOfwN+BH8jgICAgABBEGsiAySAgICAACABIQQgASEFIAEhBiABIQcgASEIIAEhCSABIQogASELIAEhDCABIQ0gASEOIAEhDwJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQCAAKAIcIhBBf2oO3QHaAQHZAQIDBAUGBwgJCgsMDQ7YAQ8Q1wEREtYBExQVFhcYGRob4AHfARwdHtUBHyAhIiMkJdQBJicoKSorLNMB0gEtLtEB0AEvMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUbbAUdISUrPAc4BS80BTMwBTU5PUFFSU1RVVldYWVpbXF1eX2BhYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ent8fX5/gAGBAYIBgwGEAYUBhgGHAYgBiQGKAYsBjAGNAY4BjwGQAZEB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QAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAAkACQAJAIBAOxwEAAQID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YWNrIGVycm9yAGBvbl9jaHVua19leHRlbnNpb25fdmFsdWVgIGNhbGxiYWNrIGVycm9yAGBvbl9zdGF0dXNfY29tcGxldGVgIGNhbGxiYWNrIGVycm9yAGBvbl92ZXJzaW9uX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fdXJsX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fY2h1bmtfY29tcGxldGVgIGNhbGxiYWNrIGVycm9yAGBvbl9oZWFkZXJfdmFsdWVfY29tcGxldGVgIGNhbGxiYWNrIGVycm9yAGBvbl9tZXNzYWdlX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fbWV0aG9kX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25faGVhZGVyX2ZpZWxkX2NvbXBsZXRlYCBjYWxsYmFjayBlcnJvcgBgb25fY2h1bmtfZXh0ZW5zaW9uX25hbWVgIGNhbGxiYWNrIGVycm9yAFVuZXhwZWN0ZWQgY2hhciBpbiB1cmwgc2VydmVyAEludmFsaWQgaGVhZGVyIHZhbHVlIGNoYXIASW52YWxpZCBoZWFkZXIgZmllbGQgY2hhcgBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX3ZlcnNpb24ASW52YWxpZCBtaW5vciB2ZXJzaW9uAEludmFsaWQgbWFqb3IgdmVyc2lvbgBFeHBlY3RlZCBzcGFjZSBhZnRlciB2ZXJzaW9uAEV4cGVjdGVkIENSTEYgYWZ0ZXIgdmVyc2lvbgBJbnZhbGlkIEhUVFAgdmVyc2lvbgBJbnZhbGlkIGhlYWRlciB0b2tlbgBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX3VybABJbnZhbGlkIGNoYXJhY3RlcnMgaW4gdXJsAFVuZXhwZWN0ZWQgc3RhcnQgY2hhciBpbiB1cmwARG91YmxlIEAgaW4gdXJsAEVtcHR5IENvbnRlbnQtTGVuZ3RoAEludmFsaWQgY2hhcmFjdGVyIGluIENvbnRlbnQtTGVuZ3RoAER1cGxpY2F0ZSBDb250ZW50LUxlbmd0aABJbnZhbGlkIGNoYXIgaW4gdXJsIHBhdGgAQ29udGVudC1MZW5ndGggY2FuJ3QgYmUgcHJlc2VudCB3aXRoIFRyYW5zZmVyLUVuY29kaW5nAEludmFsaWQgY2hhcmFjdGVyIGluIGNodW5rIHNpemUAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9oZWFkZXJfdmFsdWUAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9jaHVua19leHRlbnNpb25fdmFsdWUASW52YWxpZCBjaGFyYWN0ZXIgaW4gY2h1bmsgZXh0ZW5zaW9ucyB2YWx1ZQBNaXNzaW5nIGV4cGVjdGVkIExGIGFmdGVyIGhlYWRlciB2YWx1ZQBJbnZhbGlkIGBUcmFuc2Zlci1FbmNvZGluZ2AgaGVhZGVyIHZhbHVlAEludmFsaWQgY2hhcmFjdGVyIGluIGNodW5rIGV4dGVuc2lvbnMgcXVvdGUgdmFsdWUASW52YWxpZCBjaGFyYWN0ZXIgaW4gY2h1bmsgZXh0ZW5zaW9ucyBxdW90ZWQgdmFsdWUAUGF1c2VkIGJ5IG9uX2hlYWRlcnNfY29tcGxldGUASW52YWxpZCBFT0Ygc3RhdGUAb25fcmVzZXQgcGF1c2UAb25fY2h1bmtfaGVhZGVyIHBhdXNlAG9uX21lc3NhZ2VfYmVnaW4gcGF1c2UAb25fY2h1bmtfZXh0ZW5zaW9uX3ZhbHVlIHBhdXNlAG9uX3N0YXR1c19jb21wbGV0ZSBwYXVzZQBvbl92ZXJzaW9uX2NvbXBsZXRlIHBhdXNlAG9uX3VybF9jb21wbGV0ZSBwYXVzZQBvbl9jaHVua19jb21wbGV0ZSBwYXVzZQBvbl9oZWFkZXJfdmFsdWVfY29tcGxldGUgcGF1c2UAb25fbWVzc2FnZV9jb21wbGV0ZSBwYXVzZQBvbl9tZXRob2RfY29tcGxldGUgcGF1c2UAb25faGVhZGVyX2ZpZWxkX2NvbXBsZXRlIHBhdXNlAG9uX2NodW5rX2V4dGVuc2lvbl9uYW1lIHBhdXNlAFVuZXhwZWN0ZWQgc3BhY2UgYWZ0ZXIgc3RhcnQgbGluZQBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX2NodW5rX2V4dGVuc2lvbl9uYW1lAEludmFsaWQgY2hhcmFjdGVyIGluIGNodW5rIGV4dGVuc2lvbnMgbmFtZQBQYXVzZSBvbiBDT05ORUNUL1VwZ3JhZGUAUGF1c2Ugb24gUFJJL1VwZ3JhZGUARXhwZWN0ZWQgSFRUUC8yIENvbm5lY3Rpb24gUHJlZmFjZQBTcGFuIGNhbGxiYWNrIGVycm9yIGluIG9uX21ldGhvZABFeHBlY3RlZCBzcGFjZSBhZnRlciBtZXRob2QAU3BhbiBjYWxsYmFjayBlcnJvciBpbiBvbl9oZWFkZXJfZmllbGQAUGF1c2VkAEludmFsaWQgd29yZCBlbmNvdW50ZXJlZABJbnZhbGlkIG1ldGhvZCBlbmNvdW50ZXJlZABVbmV4cGVjdGVkIGNoYXIgaW4gdXJsIHNjaGVtYQBSZXF1ZXN0IGhhcyBpbnZhbGlkIGBUcmFuc2Zlci1FbmNvZGluZ2AAU1dJVENIX1BST1hZAFVTRV9QUk9YWQBNS0FDVElWSVRZAFVOUFJPQ0VTU0FCTEVfRU5USVRZAENPUFkATU9WRURfUEVSTUFORU5UTFkAVE9PX0VBUkxZAE5PVElGWQBGQUlMRURfREVQRU5ERU5DWQBCQURfR0FURVdBWQBQTEFZAFBVVABDSEVDS09VVABHQVRFV0FZX1RJTUVPVVQAUkVRVUVTVF9USU1FT1VUAE5FVFdPUktfQ09OTkVDVF9USU1FT1VUAENPTk5FQ1RJT05fVElNRU9VVABMT0dJTl9USU1FT1VUAE5FVFdPUktfUkVBRF9USU1FT1VUAFBPU1QATUlTRElSRUNURURfUkVRVUVTVABDTElFTlRfQ0xPU0VEX1JFUVVFU1QAQ0xJRU5UX0NMT1NFRF9MT0FEX0JBTEFOQ0VEX1JFUVVFU1QAQkFEX1JFUVVFU1QASFRUUF9SRVFVRVNUX1NFTlRfVE9fSFRUUFNfUE9SVABSRVBPUlQASU1fQV9URUFQT1QAUkVTRVRfQ09OVEVOVABOT19DT05URU5UAFBBUlRJQUxfQ09OVEVOVABIUEVfSU5WQUxJRF9DT05TVEFOVABIUEVfQ0JfUkVTRVQAR0VUAEhQRV9TVFJJQ1QAQ09ORkxJQ1QAVEVNUE9SQVJZX1JFRElSRUNUAFBFUk1BTkVOVF9SRURJUkVDVABDT05ORUNUAE1VTFRJX1NUQVRVUwBIUEVfSU5WQUxJRF9TVEFUVVMAVE9PX01BTllfUkVRVUVTVFMARUFSTFlfSElOVFMAVU5BVkFJTEFCTEVfRk9SX0xFR0FMX1JFQVNPTlMAT1BUSU9OUwBTV0lUQ0hJTkdfUFJPVE9DT0xTAFZBUklBTlRfQUxTT19ORUdPVElBVEVTAE1VTFRJUExFX0NIT0lDRVMASU5URVJOQUxfU0VSVkVSX0VSUk9SAFdFQl9TRVJWRVJfVU5LTk9XTl9FUlJPUgBSQUlMR1VOX0VSUk9SAElERU5USVRZX1BST1ZJREVSX0FVVEhFTlRJQ0FUSU9OX0VSUk9SAFNTTF9DRVJUSUZJQ0FURV9FUlJPUgBJTlZBTElEX1hfRk9SV0FSREVEX0ZPUgBTRVRfUEFSQU1FVEVSAEdFVF9QQVJBTUVURVIASFBFX1VTRVIAU0VFX09USEVSAEhQRV9DQl9DSFVOS19IRUFERVIATUtDQUxFTkRBUgBTRVRVUABXRUJfU0VSVkVSX0lTX0RPV04AVEVBUkRPV04ASFBFX0NMT1NFRF9DT05ORUNUSU9OAEhFVVJJU1RJQ19FWFBJUkFUSU9OAERJU0NPTk5FQ1RFRF9PUEVSQVRJT04ATk9OX0FVVEhPUklUQVRJVkVfSU5GT1JNQVRJT04ASFBFX0lOVkFMSURfVkVSU0lPTgBIUEVfQ0JfTUVTU0FHRV9CRUdJTgBTSVRFX0lTX0ZST1pFTgBIUEVfSU5WQUxJRF9IRUFERVJfVE9LRU4ASU5WQUxJRF9UT0tFTgBGT1JCSURERU4ARU5IQU5DRV9ZT1VSX0NBTE0ASFBFX0lOVkFMSURfVVJMAEJMT0NLRURfQllfUEFSRU5UQUxfQ09OVFJPTABNS0NPTABBQ0wASFBFX0lOVEVSTkFMAFJFUVVFU1RfSEVBREVSX0ZJRUxEU19UT09fTEFSR0VfVU5PRkZJQ0lBTABIUEVfT0sAVU5MSU5LAFVOTE9DSwBQUkkAUkVUUllfV0lUSABIUEVfSU5WQUxJRF9DT05URU5UX0xFTkdUSABIUEVfVU5FWFBFQ1RFRF9DT05URU5UX0xFTkdUSABGTFVTSABQUk9QUEFUQ0gATS1TRUFSQ0gAVVJJX1RPT19MT05HAFBST0NFU1NJTkcATUlTQ0VMTEFORU9VU19QRVJTSVNURU5UX1dBUk5JTkcATUlTQ0VMTEFORU9VU19XQVJOSU5HAEhQRV9JTlZBTElEX1RSQU5TRkVSX0VOQ09ESU5HAEV4cGVjdGVkIENSTEYASFBFX0lOVkFMSURfQ0hVTktfU0laRQBNT1ZFAENPTlRJTlVFAEhQRV9DQl9TVEFUVVNfQ09NUExFVEUASFBFX0NCX0hFQURFUlNfQ09NUExFVEUASFBFX0NCX1ZFUlNJT05fQ09NUExFVEUASFBFX0NCX1VSTF9DT01QTEVURQBIUEVfQ0JfQ0hVTktfQ09NUExFVEUASFBFX0NCX0hFQURFUl9WQUxVRV9DT01QTEVURQBIUEVfQ0JfQ0hVTktfRVhURU5TSU9OX1ZBTFVFX0NPTVBMRVRFAEhQRV9DQl9DSFVOS19FWFRFTlNJT05fTkFNRV9DT01QTEVURQBIUEVfQ0JfTUVTU0FHRV9DT01QTEVURQBIUEVfQ0JfTUVUSE9EX0NPTVBMRVRFAEhQRV9DQl9IRUFERVJfRklFTERfQ09NUExFVEUAREVMRVRFAEhQRV9JTlZBTElEX0VPRl9TVEFURQBJTlZBTElEX1NTTF9DRVJUSUZJQ0FURQBQQVVTRQBOT19SRVNQT05TRQBVTlNVUFBPUlRFRF9NRURJQV9UWVBFAEdPTkUATk9UX0FDQ0VQVEFCTEUAU0VSVklDRV9VTkFWQUlMQUJMRQBSQU5HRV9OT1RfU0FUSVNGSUFCTEUAT1JJR0lOX0lTX1VOUkVBQ0hBQkxFAFJFU1BPTlNFX0lTX1NUQUxFAFBVUkdFAE1FUkdFAFJFUVVFU1RfSEVBREVSX0ZJRUxEU19UT09fTEFSR0UAUkVRVUVTVF9IRUFERVJfVE9PX0xBUkdFAFBBWUxPQURfVE9PX0xBUkdFAElOU1VGRklDSUVOVF9TVE9SQUdFAEhQRV9QQVVTRURfVVBHUkFERQBIUEVfUEFVU0VEX0gyX1VQR1JBREUAU09VUkNFAEFOTk9VTkNFAFRSQUNFAEhQRV9VTkVYUEVDVEVEX1NQQUNFAERFU0NSSUJFAFVOU1VCU0NSSUJFAFJFQ09SRABIUEVfSU5WQUxJRF9NRVRIT0QATk9UX0ZPVU5EAFBST1BGSU5EAFVOQklORABSRUJJTkQAVU5BVVRIT1JJWkVEAE1FVEhPRF9OT1RfQUxMT1dFRABIVFRQX1ZFUlNJT05fTk9UX1NVUFBPUlRFRABBTFJFQURZX1JFUE9SVEVEAEFDQ0VQVEVEAE5PVF9JTVBMRU1FTlRFRABMT09QX0RFVEVDVEVEAEhQRV9DUl9FWFBFQ1RFRABIUEVfTEZfRVhQRUNURUQAQ1JFQVRFRABJTV9VU0VEAEhQRV9QQVVTRUQAVElNRU9VVF9PQ0NVUkVEAFBBWU1FTlRfUkVRVUlSRUQAUFJFQ09ORElUSU9OX1JFUVVJUkVEAFBST1hZX0FVVEhFTlRJQ0FUSU9OX1JFUVVJUkVEAE5FVFdPUktfQVVUSEVOVElDQVRJT05fUkVRVUlSRUQATEVOR1RIX1JFUVVJUkVEAFNTTF9DRVJUSUZJQ0FURV9SRVFVSVJFRABVUEdSQURFX1JFUVVJUkVEAFBBR0VfRVhQSVJFRABQUkVDT05ESVRJT05fRkFJTEVEAEVYUEVDVEFUSU9OX0ZBSUxFRABSRVZBTElEQVRJT05fRkFJTEVEAFNTTF9IQU5EU0hBS0VfRkFJTEVEAExPQ0tFRABUUkFOU0ZPUk1BVElPTl9BUFBMSUVEAE5PVF9NT0RJRklFRABOT1RfRVhURU5ERUQAQkFORFdJRFRIX0xJTUlUX0VYQ0VFREVEAFNJVEVfSVNfT1ZFUkxPQURFRABIRUFEAEV4cGVjdGVkIEhUVFAvAABeEwAAJhMAADAQAADwFwAAnRMAABUSAAA5FwAA8BIAAAoQAAB1EgAArRIAAIITAABPFAAAfxAAAKAVAAAjFAAAiRIAAIsUAABNFQAA1BEAAM8UAAAQGAAAyRYAANwWAADBEQAA4BcAALsUAAB0FAAAfBUAAOUUAAAIFwAAHxAAAGUVAACjFAAAKBUAAAIVAACZFQAALBAAAIsZAABPDwAA1A4AAGoQAADOEAAAAhcAAIkOAABuEwAAHBMAAGYUAABWFwAAwRMAAM0TAABsEwAAaBcAAGYXAABfFwAAIhMAAM4PAABpDgAA2A4AAGMWAADLEwAAqg4AACgXAAAmFwAAxRMAAF0WAADoEQAAZxMAAGUTAADyFgAAcxMAAB0XAAD5FgAA8xEAAM8OAADOFQAADBIAALMRAAClEQAAYRAAADIXAAC7EwAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBAgEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAgMCAgICAgAAAgIAAgIAAgICAgICAgICAgAEAAAAAAACAgICAgICAgICAgICAgICAgICAgICAgICAgAAAAICAgICAgICAgICAgICAgICAgICAgICAgICAgICAAIAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAIAAgICAgIAAAICAAICAAICAgICAgICAgIAAwAEAAAAAgICAgICAgICAgICAgICAgICAgICAgICAgIAAAACAgICAgICAgICAgICAgICAgICAgICAgICAgICAgACAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABsb3NlZWVwLWFsaXZlAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQEBAQEBAQEBAQEBAgEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQFjaHVua2VkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAQABAQEBAQAAAQEAAQEAAQEBAQEBAQEBAQAAAAAAAAABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGVjdGlvbmVudC1sZW5ndGhvbnJveHktY29ubmVjdGlvbgAAAAAAAAAAAAAAAAAAAHJhbnNmZXItZW5jb2RpbmdwZ3JhZGUNCg0KDQpTTQ0KDQpUVFAvQ0UvVFNQLwAAAAAAAAAAAAAAAAECAAEDAAAAAAAAAAAAAAAAAAAAAAAABAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAAAAAAAAAAABAgABAwAAAAAAAAAAAAAAAAAAAAAAAAQBAQUBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAAAAAAAAAQAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAQEAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQAAAAAAAAAAAAABAAACAAAAAAAAAAAAAAAAAAAAAAAAAwQAAAQEBAQEBAQEBAQEBQQEBAQEBAQEBAQEBAAEAAYHBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAAQABAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAQAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAAAAAAAAAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAEAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAgAAAAACAAAAAAAAAAAAAAAAAAAAAAADAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwAAAAAAAAMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE5PVU5DRUVDS09VVE5FQ1RFVEVDUklCRUxVU0hFVEVBRFNFQVJDSFJHRUNUSVZJVFlMRU5EQVJWRU9USUZZUFRJT05TQ0hTRUFZU1RBVENIR0VPUkRJUkVDVE9SVFJDSFBBUkFNRVRFUlVSQ0VCU0NSSUJFQVJET1dOQUNFSU5ETktDS1VCU0NSSUJFSFRUUC9BRFRQLw==' + + +/***/ }), + +/***/ 1891: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.enumToMap = void 0; +function enumToMap(obj) { + const res = {}; + Object.keys(obj).forEach((key) => { + const value = obj[key]; + if (typeof value === 'number') { + res[key] = value; + } + }); + return res; +} +exports.enumToMap = enumToMap; +//# sourceMappingURL=utils.js.map + +/***/ }), + +/***/ 6771: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { kClients } = __nccwpck_require__(2785) +const Agent = __nccwpck_require__(7890) +const { + kAgent, + kMockAgentSet, + kMockAgentGet, + kDispatches, + kIsMockActive, + kNetConnect, + kGetNetConnect, + kOptions, + kFactory +} = __nccwpck_require__(4347) +const MockClient = __nccwpck_require__(8687) +const MockPool = __nccwpck_require__(6193) +const { matchValue, buildMockOptions } = __nccwpck_require__(9323) +const { InvalidArgumentError, UndiciError } = __nccwpck_require__(8045) +const Dispatcher = __nccwpck_require__(412) +const Pluralizer = __nccwpck_require__(8891) +const PendingInterceptorsFormatter = __nccwpck_require__(6823) + +class FakeWeakRef { + constructor (value) { + this.value = value + } + + deref () { + return this.value + } +} + +class MockAgent extends Dispatcher { + constructor (opts) { + super(opts) + + this[kNetConnect] = true + this[kIsMockActive] = true + + // Instantiate Agent and encapsulate + if ((opts && opts.agent && typeof opts.agent.dispatch !== 'function')) { + throw new InvalidArgumentError('Argument opts.agent must implement Agent') + } + const agent = opts && opts.agent ? opts.agent : new Agent(opts) + this[kAgent] = agent + + this[kClients] = agent[kClients] + this[kOptions] = buildMockOptions(opts) + } + + get (origin) { + let dispatcher = this[kMockAgentGet](origin) + + if (!dispatcher) { + dispatcher = this[kFactory](origin) + this[kMockAgentSet](origin, dispatcher) + } + return dispatcher + } + + dispatch (opts, handler) { + // Call MockAgent.get to perform additional setup before dispatching as normal + this.get(opts.origin) + return this[kAgent].dispatch(opts, handler) + } + + async close () { + await this[kAgent].close() + this[kClients].clear() + } + + deactivate () { + this[kIsMockActive] = false + } + + activate () { + this[kIsMockActive] = true + } + + enableNetConnect (matcher) { + if (typeof matcher === 'string' || typeof matcher === 'function' || matcher instanceof RegExp) { + if (Array.isArray(this[kNetConnect])) { + this[kNetConnect].push(matcher) + } else { + this[kNetConnect] = [matcher] + } + } else if (typeof matcher === 'undefined') { + this[kNetConnect] = true + } else { + throw new InvalidArgumentError('Unsupported matcher. Must be one of String|Function|RegExp.') + } + } + + disableNetConnect () { + this[kNetConnect] = false + } + + // This is required to bypass issues caused by using global symbols - see: + // https://github.com/nodejs/undici/issues/1447 + get isMockActive () { + return this[kIsMockActive] + } + + [kMockAgentSet] (origin, dispatcher) { + this[kClients].set(origin, new FakeWeakRef(dispatcher)) + } + + [kFactory] (origin) { + const mockOptions = Object.assign({ agent: this }, this[kOptions]) + return this[kOptions] && this[kOptions].connections === 1 + ? new MockClient(origin, mockOptions) + : new MockPool(origin, mockOptions) + } + + [kMockAgentGet] (origin) { + // First check if we can immediately find it + const ref = this[kClients].get(origin) + if (ref) { + return ref.deref() + } + + // If the origin is not a string create a dummy parent pool and return to user + if (typeof origin !== 'string') { + const dispatcher = this[kFactory]('http://localhost:9999') + this[kMockAgentSet](origin, dispatcher) + return dispatcher + } + + // If we match, create a pool and assign the same dispatches + for (const [keyMatcher, nonExplicitRef] of Array.from(this[kClients])) { + const nonExplicitDispatcher = nonExplicitRef.deref() + if (nonExplicitDispatcher && typeof keyMatcher !== 'string' && matchValue(keyMatcher, origin)) { + const dispatcher = this[kFactory](origin) + this[kMockAgentSet](origin, dispatcher) + dispatcher[kDispatches] = nonExplicitDispatcher[kDispatches] + return dispatcher + } + } + } + + [kGetNetConnect] () { + return this[kNetConnect] + } + + pendingInterceptors () { + const mockAgentClients = this[kClients] + + return Array.from(mockAgentClients.entries()) + .flatMap(([origin, scope]) => scope.deref()[kDispatches].map(dispatch => ({ ...dispatch, origin }))) + .filter(({ pending }) => pending) + } + + assertNoPendingInterceptors ({ pendingInterceptorsFormatter = new PendingInterceptorsFormatter() } = {}) { + const pending = this.pendingInterceptors() + + if (pending.length === 0) { + return + } + + const pluralizer = new Pluralizer('interceptor', 'interceptors').pluralize(pending.length) + + throw new UndiciError(` +${pluralizer.count} ${pluralizer.noun} ${pluralizer.is} pending: + +${pendingInterceptorsFormatter.format(pending)} +`.trim()) + } +} + +module.exports = MockAgent + + +/***/ }), + +/***/ 8687: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { promisify } = __nccwpck_require__(3837) +const Client = __nccwpck_require__(3598) +const { buildMockDispatch } = __nccwpck_require__(9323) +const { + kDispatches, + kMockAgent, + kClose, + kOriginalClose, + kOrigin, + kOriginalDispatch, + kConnected +} = __nccwpck_require__(4347) +const { MockInterceptor } = __nccwpck_require__(410) +const Symbols = __nccwpck_require__(2785) +const { InvalidArgumentError } = __nccwpck_require__(8045) + +/** + * MockClient provides an API that extends the Client to influence the mockDispatches. + */ +class MockClient extends Client { + constructor (origin, opts) { + super(origin, opts) + + if (!opts || !opts.agent || typeof opts.agent.dispatch !== 'function') { + throw new InvalidArgumentError('Argument opts.agent must implement Agent') + } + + this[kMockAgent] = opts.agent + this[kOrigin] = origin + this[kDispatches] = [] + this[kConnected] = 1 + this[kOriginalDispatch] = this.dispatch + this[kOriginalClose] = this.close.bind(this) + + this.dispatch = buildMockDispatch.call(this) + this.close = this[kClose] + } + + get [Symbols.kConnected] () { + return this[kConnected] + } + + /** + * Sets up the base interceptor for mocking replies from undici. + */ + intercept (opts) { + return new MockInterceptor(opts, this[kDispatches]) + } + + async [kClose] () { + await promisify(this[kOriginalClose])() + this[kConnected] = 0 + this[kMockAgent][Symbols.kClients].delete(this[kOrigin]) + } +} + +module.exports = MockClient + + +/***/ }), + +/***/ 888: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { UndiciError } = __nccwpck_require__(8045) + +class MockNotMatchedError extends UndiciError { + constructor (message) { + super(message) + Error.captureStackTrace(this, MockNotMatchedError) + this.name = 'MockNotMatchedError' + this.message = message || 'The request does not match any registered mock dispatches' + this.code = 'UND_MOCK_ERR_MOCK_NOT_MATCHED' + } +} + +module.exports = { + MockNotMatchedError +} + + +/***/ }), + +/***/ 410: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { getResponseData, buildKey, addMockDispatch } = __nccwpck_require__(9323) +const { + kDispatches, + kDispatchKey, + kDefaultHeaders, + kDefaultTrailers, + kContentLength, + kMockDispatch +} = __nccwpck_require__(4347) +const { InvalidArgumentError } = __nccwpck_require__(8045) +const { buildURL } = __nccwpck_require__(3983) + +/** + * Defines the scope API for an interceptor reply + */ +class MockScope { + constructor (mockDispatch) { + this[kMockDispatch] = mockDispatch + } + + /** + * Delay a reply by a set amount in ms. + */ + delay (waitInMs) { + if (typeof waitInMs !== 'number' || !Number.isInteger(waitInMs) || waitInMs <= 0) { + throw new InvalidArgumentError('waitInMs must be a valid integer > 0') + } + + this[kMockDispatch].delay = waitInMs + return this + } + + /** + * For a defined reply, never mark as consumed. + */ + persist () { + this[kMockDispatch].persist = true + return this + } + + /** + * Allow one to define a reply for a set amount of matching requests. + */ + times (repeatTimes) { + if (typeof repeatTimes !== 'number' || !Number.isInteger(repeatTimes) || repeatTimes <= 0) { + throw new InvalidArgumentError('repeatTimes must be a valid integer > 0') + } + + this[kMockDispatch].times = repeatTimes + return this + } +} + +/** + * Defines an interceptor for a Mock + */ +class MockInterceptor { + constructor (opts, mockDispatches) { + if (typeof opts !== 'object') { + throw new InvalidArgumentError('opts must be an object') + } + if (typeof opts.path === 'undefined') { + throw new InvalidArgumentError('opts.path must be defined') + } + if (typeof opts.method === 'undefined') { + opts.method = 'GET' + } + // See https://github.com/nodejs/undici/issues/1245 + // As per RFC 3986, clients are not supposed to send URI + // fragments to servers when they retrieve a document, + if (typeof opts.path === 'string') { + if (opts.query) { + opts.path = buildURL(opts.path, opts.query) + } else { + // Matches https://github.com/nodejs/undici/blob/main/lib/fetch/index.js#L1811 + const parsedURL = new URL(opts.path, 'data://') + opts.path = parsedURL.pathname + parsedURL.search + } + } + if (typeof opts.method === 'string') { + opts.method = opts.method.toUpperCase() + } + + this[kDispatchKey] = buildKey(opts) + this[kDispatches] = mockDispatches + this[kDefaultHeaders] = {} + this[kDefaultTrailers] = {} + this[kContentLength] = false + } + + createMockScopeDispatchData (statusCode, data, responseOptions = {}) { + const responseData = getResponseData(data) + const contentLength = this[kContentLength] ? { 'content-length': responseData.length } : {} + const headers = { ...this[kDefaultHeaders], ...contentLength, ...responseOptions.headers } + const trailers = { ...this[kDefaultTrailers], ...responseOptions.trailers } + + return { statusCode, data, headers, trailers } + } + + validateReplyParameters (statusCode, data, responseOptions) { + if (typeof statusCode === 'undefined') { + throw new InvalidArgumentError('statusCode must be defined') + } + if (typeof data === 'undefined') { + throw new InvalidArgumentError('data must be defined') + } + if (typeof responseOptions !== 'object') { + throw new InvalidArgumentError('responseOptions must be an object') + } + } + + /** + * Mock an undici request with a defined reply. + */ + reply (replyData) { + // Values of reply aren't available right now as they + // can only be available when the reply callback is invoked. + if (typeof replyData === 'function') { + // We'll first wrap the provided callback in another function, + // this function will properly resolve the data from the callback + // when invoked. + const wrappedDefaultsCallback = (opts) => { + // Our reply options callback contains the parameter for statusCode, data and options. + const resolvedData = replyData(opts) + + // Check if it is in the right format + if (typeof resolvedData !== 'object') { + throw new InvalidArgumentError('reply options callback must return an object') + } + + const { statusCode, data = '', responseOptions = {} } = resolvedData + this.validateReplyParameters(statusCode, data, responseOptions) + // Since the values can be obtained immediately we return them + // from this higher order function that will be resolved later. + return { + ...this.createMockScopeDispatchData(statusCode, data, responseOptions) + } + } + + // Add usual dispatch data, but this time set the data parameter to function that will eventually provide data. + const newMockDispatch = addMockDispatch(this[kDispatches], this[kDispatchKey], wrappedDefaultsCallback) + return new MockScope(newMockDispatch) + } + + // We can have either one or three parameters, if we get here, + // we should have 1-3 parameters. So we spread the arguments of + // this function to obtain the parameters, since replyData will always + // just be the statusCode. + const [statusCode, data = '', responseOptions = {}] = [...arguments] + this.validateReplyParameters(statusCode, data, responseOptions) + + // Send in-already provided data like usual + const dispatchData = this.createMockScopeDispatchData(statusCode, data, responseOptions) + const newMockDispatch = addMockDispatch(this[kDispatches], this[kDispatchKey], dispatchData) + return new MockScope(newMockDispatch) + } + + /** + * Mock an undici request with a defined error. + */ + replyWithError (error) { + if (typeof error === 'undefined') { + throw new InvalidArgumentError('error must be defined') + } + + const newMockDispatch = addMockDispatch(this[kDispatches], this[kDispatchKey], { error }) + return new MockScope(newMockDispatch) + } + + /** + * Set default reply headers on the interceptor for subsequent replies + */ + defaultReplyHeaders (headers) { + if (typeof headers === 'undefined') { + throw new InvalidArgumentError('headers must be defined') + } + + this[kDefaultHeaders] = headers + return this + } + + /** + * Set default reply trailers on the interceptor for subsequent replies + */ + defaultReplyTrailers (trailers) { + if (typeof trailers === 'undefined') { + throw new InvalidArgumentError('trailers must be defined') + } + + this[kDefaultTrailers] = trailers + return this + } + + /** + * Set reply content length header for replies on the interceptor + */ + replyContentLength () { + this[kContentLength] = true + return this + } +} + +module.exports.MockInterceptor = MockInterceptor +module.exports.MockScope = MockScope + + +/***/ }), + +/***/ 6193: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { promisify } = __nccwpck_require__(3837) +const Pool = __nccwpck_require__(4634) +const { buildMockDispatch } = __nccwpck_require__(9323) +const { + kDispatches, + kMockAgent, + kClose, + kOriginalClose, + kOrigin, + kOriginalDispatch, + kConnected +} = __nccwpck_require__(4347) +const { MockInterceptor } = __nccwpck_require__(410) +const Symbols = __nccwpck_require__(2785) +const { InvalidArgumentError } = __nccwpck_require__(8045) + +/** + * MockPool provides an API that extends the Pool to influence the mockDispatches. + */ +class MockPool extends Pool { + constructor (origin, opts) { + super(origin, opts) + + if (!opts || !opts.agent || typeof opts.agent.dispatch !== 'function') { + throw new InvalidArgumentError('Argument opts.agent must implement Agent') + } + + this[kMockAgent] = opts.agent + this[kOrigin] = origin + this[kDispatches] = [] + this[kConnected] = 1 + this[kOriginalDispatch] = this.dispatch + this[kOriginalClose] = this.close.bind(this) + + this.dispatch = buildMockDispatch.call(this) + this.close = this[kClose] + } + + get [Symbols.kConnected] () { + return this[kConnected] + } + + /** + * Sets up the base interceptor for mocking replies from undici. + */ + intercept (opts) { + return new MockInterceptor(opts, this[kDispatches]) + } + + async [kClose] () { + await promisify(this[kOriginalClose])() + this[kConnected] = 0 + this[kMockAgent][Symbols.kClients].delete(this[kOrigin]) + } +} + +module.exports = MockPool + + +/***/ }), + +/***/ 4347: +/***/ ((module) => { + +"use strict"; + + +module.exports = { + kAgent: Symbol('agent'), + kOptions: Symbol('options'), + kFactory: Symbol('factory'), + kDispatches: Symbol('dispatches'), + kDispatchKey: Symbol('dispatch key'), + kDefaultHeaders: Symbol('default headers'), + kDefaultTrailers: Symbol('default trailers'), + kContentLength: Symbol('content length'), + kMockAgent: Symbol('mock agent'), + kMockAgentSet: Symbol('mock agent set'), + kMockAgentGet: Symbol('mock agent get'), + kMockDispatch: Symbol('mock dispatch'), + kClose: Symbol('close'), + kOriginalClose: Symbol('original agent close'), + kOrigin: Symbol('origin'), + kIsMockActive: Symbol('is mock active'), + kNetConnect: Symbol('net connect'), + kGetNetConnect: Symbol('get net connect'), + kConnected: Symbol('connected') +} + + +/***/ }), + +/***/ 9323: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { MockNotMatchedError } = __nccwpck_require__(888) +const { + kDispatches, + kMockAgent, + kOriginalDispatch, + kOrigin, + kGetNetConnect +} = __nccwpck_require__(4347) +const { buildURL, nop } = __nccwpck_require__(3983) +const { STATUS_CODES } = __nccwpck_require__(3685) +const { + types: { + isPromise + } +} = __nccwpck_require__(3837) + +function matchValue (match, value) { + if (typeof match === 'string') { + return match === value + } + if (match instanceof RegExp) { + return match.test(value) + } + if (typeof match === 'function') { + return match(value) === true + } + return false +} + +function lowerCaseEntries (headers) { + return Object.fromEntries( + Object.entries(headers).map(([headerName, headerValue]) => { + return [headerName.toLocaleLowerCase(), headerValue] + }) + ) +} + +/** + * @param {import('../../index').Headers|string[]|Record} headers + * @param {string} key + */ +function getHeaderByName (headers, key) { + if (Array.isArray(headers)) { + for (let i = 0; i < headers.length; i += 2) { + if (headers[i].toLocaleLowerCase() === key.toLocaleLowerCase()) { + return headers[i + 1] + } + } + + return undefined + } else if (typeof headers.get === 'function') { + return headers.get(key) + } else { + return lowerCaseEntries(headers)[key.toLocaleLowerCase()] + } +} + +/** @param {string[]} headers */ +function buildHeadersFromArray (headers) { // fetch HeadersList + const clone = headers.slice() + const entries = [] + for (let index = 0; index < clone.length; index += 2) { + entries.push([clone[index], clone[index + 1]]) + } + return Object.fromEntries(entries) +} + +function matchHeaders (mockDispatch, headers) { + if (typeof mockDispatch.headers === 'function') { + if (Array.isArray(headers)) { // fetch HeadersList + headers = buildHeadersFromArray(headers) + } + return mockDispatch.headers(headers ? lowerCaseEntries(headers) : {}) + } + if (typeof mockDispatch.headers === 'undefined') { + return true + } + if (typeof headers !== 'object' || typeof mockDispatch.headers !== 'object') { + return false + } + + for (const [matchHeaderName, matchHeaderValue] of Object.entries(mockDispatch.headers)) { + const headerValue = getHeaderByName(headers, matchHeaderName) + + if (!matchValue(matchHeaderValue, headerValue)) { + return false + } + } + return true +} + +function safeUrl (path) { + if (typeof path !== 'string') { + return path + } + + const pathSegments = path.split('?') + + if (pathSegments.length !== 2) { + return path + } + + const qp = new URLSearchParams(pathSegments.pop()) + qp.sort() + return [...pathSegments, qp.toString()].join('?') +} + +function matchKey (mockDispatch, { path, method, body, headers }) { + const pathMatch = matchValue(mockDispatch.path, path) + const methodMatch = matchValue(mockDispatch.method, method) + const bodyMatch = typeof mockDispatch.body !== 'undefined' ? matchValue(mockDispatch.body, body) : true + const headersMatch = matchHeaders(mockDispatch, headers) + return pathMatch && methodMatch && bodyMatch && headersMatch +} + +function getResponseData (data) { + if (Buffer.isBuffer(data)) { + return data + } else if (typeof data === 'object') { + return JSON.stringify(data) + } else { + return data.toString() + } +} + +function getMockDispatch (mockDispatches, key) { + const basePath = key.query ? buildURL(key.path, key.query) : key.path + const resolvedPath = typeof basePath === 'string' ? safeUrl(basePath) : basePath + + // Match path + let matchedMockDispatches = mockDispatches.filter(({ consumed }) => !consumed).filter(({ path }) => matchValue(safeUrl(path), resolvedPath)) + if (matchedMockDispatches.length === 0) { + throw new MockNotMatchedError(`Mock dispatch not matched for path '${resolvedPath}'`) + } + + // Match method + matchedMockDispatches = matchedMockDispatches.filter(({ method }) => matchValue(method, key.method)) + if (matchedMockDispatches.length === 0) { + throw new MockNotMatchedError(`Mock dispatch not matched for method '${key.method}'`) + } + + // Match body + matchedMockDispatches = matchedMockDispatches.filter(({ body }) => typeof body !== 'undefined' ? matchValue(body, key.body) : true) + if (matchedMockDispatches.length === 0) { + throw new MockNotMatchedError(`Mock dispatch not matched for body '${key.body}'`) + } + + // Match headers + matchedMockDispatches = matchedMockDispatches.filter((mockDispatch) => matchHeaders(mockDispatch, key.headers)) + if (matchedMockDispatches.length === 0) { + throw new MockNotMatchedError(`Mock dispatch not matched for headers '${typeof key.headers === 'object' ? JSON.stringify(key.headers) : key.headers}'`) + } + + return matchedMockDispatches[0] +} + +function addMockDispatch (mockDispatches, key, data) { + const baseData = { timesInvoked: 0, times: 1, persist: false, consumed: false } + const replyData = typeof data === 'function' ? { callback: data } : { ...data } + const newMockDispatch = { ...baseData, ...key, pending: true, data: { error: null, ...replyData } } + mockDispatches.push(newMockDispatch) + return newMockDispatch +} + +function deleteMockDispatch (mockDispatches, key) { + const index = mockDispatches.findIndex(dispatch => { + if (!dispatch.consumed) { + return false + } + return matchKey(dispatch, key) + }) + if (index !== -1) { + mockDispatches.splice(index, 1) + } +} + +function buildKey (opts) { + const { path, method, body, headers, query } = opts + return { + path, + method, + body, + headers, + query + } +} + +function generateKeyValues (data) { + return Object.entries(data).reduce((keyValuePairs, [key, value]) => [ + ...keyValuePairs, + Buffer.from(`${key}`), + Array.isArray(value) ? value.map(x => Buffer.from(`${x}`)) : Buffer.from(`${value}`) + ], []) +} + +/** + * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status + * @param {number} statusCode + */ +function getStatusText (statusCode) { + return STATUS_CODES[statusCode] || 'unknown' +} + +async function getResponse (body) { + const buffers = [] + for await (const data of body) { + buffers.push(data) + } + return Buffer.concat(buffers).toString('utf8') +} + +/** + * Mock dispatch function used to simulate undici dispatches + */ +function mockDispatch (opts, handler) { + // Get mock dispatch from built key + const key = buildKey(opts) + const mockDispatch = getMockDispatch(this[kDispatches], key) + + mockDispatch.timesInvoked++ + + // Here's where we resolve a callback if a callback is present for the dispatch data. + if (mockDispatch.data.callback) { + mockDispatch.data = { ...mockDispatch.data, ...mockDispatch.data.callback(opts) } + } + + // Parse mockDispatch data + const { data: { statusCode, data, headers, trailers, error }, delay, persist } = mockDispatch + const { timesInvoked, times } = mockDispatch + + // If it's used up and not persistent, mark as consumed + mockDispatch.consumed = !persist && timesInvoked >= times + mockDispatch.pending = timesInvoked < times + + // If specified, trigger dispatch error + if (error !== null) { + deleteMockDispatch(this[kDispatches], key) + handler.onError(error) + return true + } + + // Handle the request with a delay if necessary + if (typeof delay === 'number' && delay > 0) { + setTimeout(() => { + handleReply(this[kDispatches]) + }, delay) + } else { + handleReply(this[kDispatches]) + } + + function handleReply (mockDispatches, _data = data) { + // fetch's HeadersList is a 1D string array + const optsHeaders = Array.isArray(opts.headers) + ? buildHeadersFromArray(opts.headers) + : opts.headers + const body = typeof _data === 'function' + ? _data({ ...opts, headers: optsHeaders }) + : _data + + // util.types.isPromise is likely needed for jest. + if (isPromise(body)) { + // If handleReply is asynchronous, throwing an error + // in the callback will reject the promise, rather than + // synchronously throw the error, which breaks some tests. + // Rather, we wait for the callback to resolve if it is a + // promise, and then re-run handleReply with the new body. + body.then((newData) => handleReply(mockDispatches, newData)) + return + } + + const responseData = getResponseData(body) + const responseHeaders = generateKeyValues(headers) + const responseTrailers = generateKeyValues(trailers) + + handler.abort = nop + handler.onHeaders(statusCode, responseHeaders, resume, getStatusText(statusCode)) + handler.onData(Buffer.from(responseData)) + handler.onComplete(responseTrailers) + deleteMockDispatch(mockDispatches, key) + } + + function resume () {} + + return true +} + +function buildMockDispatch () { + const agent = this[kMockAgent] + const origin = this[kOrigin] + const originalDispatch = this[kOriginalDispatch] + + return function dispatch (opts, handler) { + if (agent.isMockActive) { + try { + mockDispatch.call(this, opts, handler) + } catch (error) { + if (error instanceof MockNotMatchedError) { + const netConnect = agent[kGetNetConnect]() + if (netConnect === false) { + throw new MockNotMatchedError(`${error.message}: subsequent request to origin ${origin} was not allowed (net.connect disabled)`) + } + if (checkNetConnect(netConnect, origin)) { + originalDispatch.call(this, opts, handler) + } else { + throw new MockNotMatchedError(`${error.message}: subsequent request to origin ${origin} was not allowed (net.connect is not enabled for this origin)`) + } + } else { + throw error + } + } + } else { + originalDispatch.call(this, opts, handler) + } + } +} + +function checkNetConnect (netConnect, origin) { + const url = new URL(origin) + if (netConnect === true) { + return true + } else if (Array.isArray(netConnect) && netConnect.some((matcher) => matchValue(matcher, url.host))) { + return true + } + return false +} + +function buildMockOptions (opts) { + if (opts) { + const { agent, ...mockOptions } = opts + return mockOptions + } +} + +module.exports = { + getResponseData, + getMockDispatch, + addMockDispatch, + deleteMockDispatch, + buildKey, + generateKeyValues, + matchValue, + getResponse, + getStatusText, + mockDispatch, + buildMockDispatch, + checkNetConnect, + buildMockOptions, + getHeaderByName +} + + +/***/ }), + +/***/ 6823: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { Transform } = __nccwpck_require__(2781) +const { Console } = __nccwpck_require__(6206) + +/** + * Gets the output of `console.table(…)` as a string. + */ +module.exports = class PendingInterceptorsFormatter { + constructor ({ disableColors } = {}) { + this.transform = new Transform({ + transform (chunk, _enc, cb) { + cb(null, chunk) + } + }) + + this.logger = new Console({ + stdout: this.transform, + inspectOptions: { + colors: !disableColors && !process.env.CI + } + }) + } + + format (pendingInterceptors) { + const withPrettyHeaders = pendingInterceptors.map( + ({ method, path, data: { statusCode }, persist, times, timesInvoked, origin }) => ({ + Method: method, + Origin: origin, + Path: path, + 'Status code': statusCode, + Persistent: persist ? '✅' : '❌', + Invocations: timesInvoked, + Remaining: persist ? Infinity : times - timesInvoked + })) + + this.logger.table(withPrettyHeaders) + return this.transform.read().toString() + } +} + + +/***/ }), + +/***/ 8891: +/***/ ((module) => { + +"use strict"; + + +const singulars = { + pronoun: 'it', + is: 'is', + was: 'was', + this: 'this' +} + +const plurals = { + pronoun: 'they', + is: 'are', + was: 'were', + this: 'these' +} + +module.exports = class Pluralizer { + constructor (singular, plural) { + this.singular = singular + this.plural = plural + } + + pluralize (count) { + const one = count === 1 + const keys = one ? singulars : plurals + const noun = one ? this.singular : this.plural + return { ...keys, count, noun } + } +} + + +/***/ }), + +/***/ 8266: +/***/ ((module) => { + +"use strict"; +/* eslint-disable */ + + + +// Extracted from node/lib/internal/fixed_queue.js + +// Currently optimal queue size, tested on V8 6.0 - 6.6. Must be power of two. +const kSize = 2048; +const kMask = kSize - 1; + +// The FixedQueue is implemented as a singly-linked list of fixed-size +// circular buffers. It looks something like this: +// +// head tail +// | | +// v v +// +-----------+ <-----\ +-----------+ <------\ +-----------+ +// | [null] | \----- | next | \------- | next | +// +-----------+ +-----------+ +-----------+ +// | item | <-- bottom | item | <-- bottom | [empty] | +// | item | | item | | [empty] | +// | item | | item | | [empty] | +// | item | | item | | [empty] | +// | item | | item | bottom --> | item | +// | item | | item | | item | +// | ... | | ... | | ... | +// | item | | item | | item | +// | item | | item | | item | +// | [empty] | <-- top | item | | item | +// | [empty] | | item | | item | +// | [empty] | | [empty] | <-- top top --> | [empty] | +// +-----------+ +-----------+ +-----------+ +// +// Or, if there is only one circular buffer, it looks something +// like either of these: +// +// head tail head tail +// | | | | +// v v v v +// +-----------+ +-----------+ +// | [null] | | [null] | +// +-----------+ +-----------+ +// | [empty] | | item | +// | [empty] | | item | +// | item | <-- bottom top --> | [empty] | +// | item | | [empty] | +// | [empty] | <-- top bottom --> | item | +// | [empty] | | item | +// +-----------+ +-----------+ +// +// Adding a value means moving `top` forward by one, removing means +// moving `bottom` forward by one. After reaching the end, the queue +// wraps around. +// +// When `top === bottom` the current queue is empty and when +// `top + 1 === bottom` it's full. This wastes a single space of storage +// but allows much quicker checks. + +class FixedCircularBuffer { + constructor() { + this.bottom = 0; + this.top = 0; + this.list = new Array(kSize); + this.next = null; + } + + isEmpty() { + return this.top === this.bottom; + } + + isFull() { + return ((this.top + 1) & kMask) === this.bottom; + } + + push(data) { + this.list[this.top] = data; + this.top = (this.top + 1) & kMask; + } + + shift() { + const nextItem = this.list[this.bottom]; + if (nextItem === undefined) + return null; + this.list[this.bottom] = undefined; + this.bottom = (this.bottom + 1) & kMask; + return nextItem; + } +} + +module.exports = class FixedQueue { + constructor() { + this.head = this.tail = new FixedCircularBuffer(); + } + + isEmpty() { + return this.head.isEmpty(); + } + + push(data) { + if (this.head.isFull()) { + // Head is full: Creates a new queue, sets the old queue's `.next` to it, + // and sets it as the new main queue. + this.head = this.head.next = new FixedCircularBuffer(); + } + this.head.push(data); + } + + shift() { + const tail = this.tail; + const next = tail.shift(); + if (tail.isEmpty() && tail.next !== null) { + // If there is another queue, it forms the new tail. + this.tail = tail.next; + } + return next; + } +}; + + +/***/ }), + +/***/ 3198: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const DispatcherBase = __nccwpck_require__(4839) +const FixedQueue = __nccwpck_require__(8266) +const { kConnected, kSize, kRunning, kPending, kQueued, kBusy, kFree, kUrl, kClose, kDestroy, kDispatch } = __nccwpck_require__(2785) +const PoolStats = __nccwpck_require__(9689) + +const kClients = Symbol('clients') +const kNeedDrain = Symbol('needDrain') +const kQueue = Symbol('queue') +const kClosedResolve = Symbol('closed resolve') +const kOnDrain = Symbol('onDrain') +const kOnConnect = Symbol('onConnect') +const kOnDisconnect = Symbol('onDisconnect') +const kOnConnectionError = Symbol('onConnectionError') +const kGetDispatcher = Symbol('get dispatcher') +const kAddClient = Symbol('add client') +const kRemoveClient = Symbol('remove client') +const kStats = Symbol('stats') + +class PoolBase extends DispatcherBase { + constructor () { + super() + + this[kQueue] = new FixedQueue() + this[kClients] = [] + this[kQueued] = 0 + + const pool = this + + this[kOnDrain] = function onDrain (origin, targets) { + const queue = pool[kQueue] + + let needDrain = false + + while (!needDrain) { + const item = queue.shift() + if (!item) { + break + } + pool[kQueued]-- + needDrain = !this.dispatch(item.opts, item.handler) + } + + this[kNeedDrain] = needDrain + + if (!this[kNeedDrain] && pool[kNeedDrain]) { + pool[kNeedDrain] = false + pool.emit('drain', origin, [pool, ...targets]) + } + + if (pool[kClosedResolve] && queue.isEmpty()) { + Promise + .all(pool[kClients].map(c => c.close())) + .then(pool[kClosedResolve]) + } + } + + this[kOnConnect] = (origin, targets) => { + pool.emit('connect', origin, [pool, ...targets]) + } + + this[kOnDisconnect] = (origin, targets, err) => { + pool.emit('disconnect', origin, [pool, ...targets], err) + } + + this[kOnConnectionError] = (origin, targets, err) => { + pool.emit('connectionError', origin, [pool, ...targets], err) + } + + this[kStats] = new PoolStats(this) + } + + get [kBusy] () { + return this[kNeedDrain] + } + + get [kConnected] () { + return this[kClients].filter(client => client[kConnected]).length + } + + get [kFree] () { + return this[kClients].filter(client => client[kConnected] && !client[kNeedDrain]).length + } + + get [kPending] () { + let ret = this[kQueued] + for (const { [kPending]: pending } of this[kClients]) { + ret += pending + } + return ret + } + + get [kRunning] () { + let ret = 0 + for (const { [kRunning]: running } of this[kClients]) { + ret += running + } + return ret + } + + get [kSize] () { + let ret = this[kQueued] + for (const { [kSize]: size } of this[kClients]) { + ret += size + } + return ret + } + + get stats () { + return this[kStats] + } + + async [kClose] () { + if (this[kQueue].isEmpty()) { + return Promise.all(this[kClients].map(c => c.close())) + } else { + return new Promise((resolve) => { + this[kClosedResolve] = resolve + }) + } + } + + async [kDestroy] (err) { + while (true) { + const item = this[kQueue].shift() + if (!item) { + break + } + item.handler.onError(err) + } + + return Promise.all(this[kClients].map(c => c.destroy(err))) + } + + [kDispatch] (opts, handler) { + const dispatcher = this[kGetDispatcher]() + + if (!dispatcher) { + this[kNeedDrain] = true + this[kQueue].push({ opts, handler }) + this[kQueued]++ + } else if (!dispatcher.dispatch(opts, handler)) { + dispatcher[kNeedDrain] = true + this[kNeedDrain] = !this[kGetDispatcher]() + } + + return !this[kNeedDrain] + } + + [kAddClient] (client) { + client + .on('drain', this[kOnDrain]) + .on('connect', this[kOnConnect]) + .on('disconnect', this[kOnDisconnect]) + .on('connectionError', this[kOnConnectionError]) + + this[kClients].push(client) + + if (this[kNeedDrain]) { + process.nextTick(() => { + if (this[kNeedDrain]) { + this[kOnDrain](client[kUrl], [this, client]) + } + }) + } + + return this + } + + [kRemoveClient] (client) { + client.close(() => { + const idx = this[kClients].indexOf(client) + if (idx !== -1) { + this[kClients].splice(idx, 1) + } + }) + + this[kNeedDrain] = this[kClients].some(dispatcher => ( + !dispatcher[kNeedDrain] && + dispatcher.closed !== true && + dispatcher.destroyed !== true + )) + } +} + +module.exports = { + PoolBase, + kClients, + kNeedDrain, + kAddClient, + kRemoveClient, + kGetDispatcher +} + + +/***/ }), + +/***/ 9689: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +const { kFree, kConnected, kPending, kQueued, kRunning, kSize } = __nccwpck_require__(2785) +const kPool = Symbol('pool') + +class PoolStats { + constructor (pool) { + this[kPool] = pool + } + + get connected () { + return this[kPool][kConnected] + } + + get free () { + return this[kPool][kFree] + } + + get pending () { + return this[kPool][kPending] + } + + get queued () { + return this[kPool][kQueued] + } + + get running () { + return this[kPool][kRunning] + } + + get size () { + return this[kPool][kSize] + } +} + +module.exports = PoolStats + + +/***/ }), + +/***/ 4634: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { + PoolBase, + kClients, + kNeedDrain, + kAddClient, + kGetDispatcher +} = __nccwpck_require__(3198) +const Client = __nccwpck_require__(3598) +const { + InvalidArgumentError +} = __nccwpck_require__(8045) +const util = __nccwpck_require__(3983) +const { kUrl, kInterceptors } = __nccwpck_require__(2785) +const buildConnector = __nccwpck_require__(2067) + +const kOptions = Symbol('options') +const kConnections = Symbol('connections') +const kFactory = Symbol('factory') + +function defaultFactory (origin, opts) { + return new Client(origin, opts) +} + +class Pool extends PoolBase { + constructor (origin, { + connections, + factory = defaultFactory, + connect, + connectTimeout, + tls, + maxCachedSessions, + socketPath, + autoSelectFamily, + autoSelectFamilyAttemptTimeout, + allowH2, + ...options + } = {}) { + super() + + if (connections != null && (!Number.isFinite(connections) || connections < 0)) { + throw new InvalidArgumentError('invalid connections') + } + + if (typeof factory !== 'function') { + throw new InvalidArgumentError('factory must be a function.') + } + + if (connect != null && typeof connect !== 'function' && typeof connect !== 'object') { + throw new InvalidArgumentError('connect must be a function or an object') + } + + if (typeof connect !== 'function') { + connect = buildConnector({ + ...tls, + maxCachedSessions, + allowH2, + socketPath, + timeout: connectTimeout, + ...(util.nodeHasAutoSelectFamily && autoSelectFamily ? { autoSelectFamily, autoSelectFamilyAttemptTimeout } : undefined), + ...connect + }) + } + + this[kInterceptors] = options.interceptors && options.interceptors.Pool && Array.isArray(options.interceptors.Pool) + ? options.interceptors.Pool + : [] + this[kConnections] = connections || null + this[kUrl] = util.parseOrigin(origin) + this[kOptions] = { ...util.deepClone(options), connect, allowH2 } + this[kOptions].interceptors = options.interceptors + ? { ...options.interceptors } + : undefined + this[kFactory] = factory + } + + [kGetDispatcher] () { + let dispatcher = this[kClients].find(dispatcher => !dispatcher[kNeedDrain]) + + if (dispatcher) { + return dispatcher + } + + if (!this[kConnections] || this[kClients].length < this[kConnections]) { + dispatcher = this[kFactory](this[kUrl], this[kOptions]) + this[kAddClient](dispatcher) + } + + return dispatcher + } +} + +module.exports = Pool + + +/***/ }), + +/***/ 7858: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { kProxy, kClose, kDestroy, kInterceptors } = __nccwpck_require__(2785) +const { URL } = __nccwpck_require__(7310) +const Agent = __nccwpck_require__(7890) +const Pool = __nccwpck_require__(4634) +const DispatcherBase = __nccwpck_require__(4839) +const { InvalidArgumentError, RequestAbortedError } = __nccwpck_require__(8045) +const buildConnector = __nccwpck_require__(2067) + +const kAgent = Symbol('proxy agent') +const kClient = Symbol('proxy client') +const kProxyHeaders = Symbol('proxy headers') +const kRequestTls = Symbol('request tls settings') +const kProxyTls = Symbol('proxy tls settings') +const kConnectEndpoint = Symbol('connect endpoint function') + +function defaultProtocolPort (protocol) { + return protocol === 'https:' ? 443 : 80 +} + +function buildProxyOptions (opts) { + if (typeof opts === 'string') { + opts = { uri: opts } + } + + if (!opts || !opts.uri) { + throw new InvalidArgumentError('Proxy opts.uri is mandatory') + } + + return { + uri: opts.uri, + protocol: opts.protocol || 'https' + } +} + +function defaultFactory (origin, opts) { + return new Pool(origin, opts) +} + +class ProxyAgent extends DispatcherBase { + constructor (opts) { + super(opts) + this[kProxy] = buildProxyOptions(opts) + this[kAgent] = new Agent(opts) + this[kInterceptors] = opts.interceptors && opts.interceptors.ProxyAgent && Array.isArray(opts.interceptors.ProxyAgent) + ? opts.interceptors.ProxyAgent + : [] + + if (typeof opts === 'string') { + opts = { uri: opts } + } + + if (!opts || !opts.uri) { + throw new InvalidArgumentError('Proxy opts.uri is mandatory') + } + + const { clientFactory = defaultFactory } = opts + + if (typeof clientFactory !== 'function') { + throw new InvalidArgumentError('Proxy opts.clientFactory must be a function.') + } + + this[kRequestTls] = opts.requestTls + this[kProxyTls] = opts.proxyTls + this[kProxyHeaders] = opts.headers || {} + + if (opts.auth && opts.token) { + throw new InvalidArgumentError('opts.auth cannot be used in combination with opts.token') + } else if (opts.auth) { + /* @deprecated in favour of opts.token */ + this[kProxyHeaders]['proxy-authorization'] = `Basic ${opts.auth}` + } else if (opts.token) { + this[kProxyHeaders]['proxy-authorization'] = opts.token + } + + const resolvedUrl = new URL(opts.uri) + const { origin, port, host } = resolvedUrl + + const connect = buildConnector({ ...opts.proxyTls }) + this[kConnectEndpoint] = buildConnector({ ...opts.requestTls }) + this[kClient] = clientFactory(resolvedUrl, { connect }) + this[kAgent] = new Agent({ + ...opts, + connect: async (opts, callback) => { + let requestedHost = opts.host + if (!opts.port) { + requestedHost += `:${defaultProtocolPort(opts.protocol)}` + } + try { + const { socket, statusCode } = await this[kClient].connect({ + origin, + port, + path: requestedHost, + signal: opts.signal, + headers: { + ...this[kProxyHeaders], + host + } + }) + if (statusCode !== 200) { + socket.on('error', () => {}).destroy() + callback(new RequestAbortedError('Proxy response !== 200 when HTTP Tunneling')) + } + if (opts.protocol !== 'https:') { + callback(null, socket) + return + } + let servername + if (this[kRequestTls]) { + servername = this[kRequestTls].servername + } else { + servername = opts.servername + } + this[kConnectEndpoint]({ ...opts, servername, httpSocket: socket }, callback) + } catch (err) { + callback(err) + } + } + }) + } + + dispatch (opts, handler) { + const { host } = new URL(opts.origin) + const headers = buildHeaders(opts.headers) + throwIfProxyAuthIsSent(headers) + return this[kAgent].dispatch( + { + ...opts, + headers: { + ...headers, + host + } + }, + handler + ) + } + + async [kClose] () { + await this[kAgent].close() + await this[kClient].close() + } + + async [kDestroy] () { + await this[kAgent].destroy() + await this[kClient].destroy() + } +} + +/** + * @param {string[] | Record} headers + * @returns {Record} + */ +function buildHeaders (headers) { + // When using undici.fetch, the headers list is stored + // as an array. + if (Array.isArray(headers)) { + /** @type {Record} */ + const headersPair = {} + + for (let i = 0; i < headers.length; i += 2) { + headersPair[headers[i]] = headers[i + 1] + } + + return headersPair + } + + return headers +} + +/** + * @param {Record} headers + * + * Previous versions of ProxyAgent suggests the Proxy-Authorization in request headers + * Nevertheless, it was changed and to avoid a security vulnerability by end users + * this check was created. + * It should be removed in the next major version for performance reasons + */ +function throwIfProxyAuthIsSent (headers) { + const existProxyAuth = headers && Object.keys(headers) + .find((key) => key.toLowerCase() === 'proxy-authorization') + if (existProxyAuth) { + throw new InvalidArgumentError('Proxy-Authorization should be sent in ProxyAgent constructor') + } +} + +module.exports = ProxyAgent + + +/***/ }), + +/***/ 9459: +/***/ ((module) => { + +"use strict"; + + +let fastNow = Date.now() +let fastNowTimeout + +const fastTimers = [] + +function onTimeout () { + fastNow = Date.now() + + let len = fastTimers.length + let idx = 0 + while (idx < len) { + const timer = fastTimers[idx] + + if (timer.state === 0) { + timer.state = fastNow + timer.delay + } else if (timer.state > 0 && fastNow >= timer.state) { + timer.state = -1 + timer.callback(timer.opaque) + } + + if (timer.state === -1) { + timer.state = -2 + if (idx !== len - 1) { + fastTimers[idx] = fastTimers.pop() + } else { + fastTimers.pop() + } + len -= 1 + } else { + idx += 1 + } + } + + if (fastTimers.length > 0) { + refreshTimeout() + } +} + +function refreshTimeout () { + if (fastNowTimeout && fastNowTimeout.refresh) { + fastNowTimeout.refresh() + } else { + clearTimeout(fastNowTimeout) + fastNowTimeout = setTimeout(onTimeout, 1e3) + if (fastNowTimeout.unref) { + fastNowTimeout.unref() + } + } +} + +class Timeout { + constructor (callback, delay, opaque) { + this.callback = callback + this.delay = delay + this.opaque = opaque + + // -2 not in timer list + // -1 in timer list but inactive + // 0 in timer list waiting for time + // > 0 in timer list waiting for time to expire + this.state = -2 + + this.refresh() + } + + refresh () { + if (this.state === -2) { + fastTimers.push(this) + if (!fastNowTimeout || fastTimers.length === 1) { + refreshTimeout() + } + } + + this.state = 0 + } + + clear () { + this.state = -1 + } +} + +module.exports = { + setTimeout (callback, delay, opaque) { + return delay < 1e3 + ? setTimeout(callback, delay, opaque) + : new Timeout(callback, delay, opaque) + }, + clearTimeout (timeout) { + if (timeout instanceof Timeout) { + timeout.clear() + } else { + clearTimeout(timeout) + } + } +} + + +/***/ }), + +/***/ 5354: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const diagnosticsChannel = __nccwpck_require__(7643) +const { uid, states } = __nccwpck_require__(9188) +const { + kReadyState, + kSentClose, + kByteParser, + kReceivedClose +} = __nccwpck_require__(7578) +const { fireEvent, failWebsocketConnection } = __nccwpck_require__(5515) +const { CloseEvent } = __nccwpck_require__(2611) +const { makeRequest } = __nccwpck_require__(8359) +const { fetching } = __nccwpck_require__(4881) +const { Headers } = __nccwpck_require__(554) +const { getGlobalDispatcher } = __nccwpck_require__(1892) +const { kHeadersList } = __nccwpck_require__(2785) + +const channels = {} +channels.open = diagnosticsChannel.channel('undici:websocket:open') +channels.close = diagnosticsChannel.channel('undici:websocket:close') +channels.socketError = diagnosticsChannel.channel('undici:websocket:socket_error') + +/** @type {import('crypto')} */ +let crypto +try { + crypto = __nccwpck_require__(6113) +} catch { + +} + +/** + * @see https://websockets.spec.whatwg.org/#concept-websocket-establish + * @param {URL} url + * @param {string|string[]} protocols + * @param {import('./websocket').WebSocket} ws + * @param {(response: any) => void} onEstablish + * @param {Partial} options + */ +function establishWebSocketConnection (url, protocols, ws, onEstablish, options) { + // 1. Let requestURL be a copy of url, with its scheme set to "http", if url’s + // scheme is "ws", and to "https" otherwise. + const requestURL = url + + requestURL.protocol = url.protocol === 'ws:' ? 'http:' : 'https:' + + // 2. Let request be a new request, whose URL is requestURL, client is client, + // service-workers mode is "none", referrer is "no-referrer", mode is + // "websocket", credentials mode is "include", cache mode is "no-store" , + // and redirect mode is "error". + const request = makeRequest({ + urlList: [requestURL], + serviceWorkers: 'none', + referrer: 'no-referrer', + mode: 'websocket', + credentials: 'include', + cache: 'no-store', + redirect: 'error' + }) + + // Note: undici extension, allow setting custom headers. + if (options.headers) { + const headersList = new Headers(options.headers)[kHeadersList] + + request.headersList = headersList + } + + // 3. Append (`Upgrade`, `websocket`) to request’s header list. + // 4. Append (`Connection`, `Upgrade`) to request’s header list. + // Note: both of these are handled by undici currently. + // https://github.com/nodejs/undici/blob/68c269c4144c446f3f1220951338daef4a6b5ec4/lib/client.js#L1397 + + // 5. Let keyValue be a nonce consisting of a randomly selected + // 16-byte value that has been forgiving-base64-encoded and + // isomorphic encoded. + const keyValue = crypto.randomBytes(16).toString('base64') + + // 6. Append (`Sec-WebSocket-Key`, keyValue) to request’s + // header list. + request.headersList.append('sec-websocket-key', keyValue) + + // 7. Append (`Sec-WebSocket-Version`, `13`) to request’s + // header list. + request.headersList.append('sec-websocket-version', '13') + + // 8. For each protocol in protocols, combine + // (`Sec-WebSocket-Protocol`, protocol) in request’s header + // list. + for (const protocol of protocols) { + request.headersList.append('sec-websocket-protocol', protocol) + } + + // 9. Let permessageDeflate be a user-agent defined + // "permessage-deflate" extension header value. + // https://github.com/mozilla/gecko-dev/blob/ce78234f5e653a5d3916813ff990f053510227bc/netwerk/protocol/websocket/WebSocketChannel.cpp#L2673 + // TODO: enable once permessage-deflate is supported + const permessageDeflate = '' // 'permessage-deflate; 15' + + // 10. Append (`Sec-WebSocket-Extensions`, permessageDeflate) to + // request’s header list. + // request.headersList.append('sec-websocket-extensions', permessageDeflate) + + // 11. Fetch request with useParallelQueue set to true, and + // processResponse given response being these steps: + const controller = fetching({ + request, + useParallelQueue: true, + dispatcher: options.dispatcher ?? getGlobalDispatcher(), + processResponse (response) { + // 1. If response is a network error or its status is not 101, + // fail the WebSocket connection. + if (response.type === 'error' || response.status !== 101) { + failWebsocketConnection(ws, 'Received network error or non-101 status code.') + return + } + + // 2. If protocols is not the empty list and extracting header + // list values given `Sec-WebSocket-Protocol` and response’s + // header list results in null, failure, or the empty byte + // sequence, then fail the WebSocket connection. + if (protocols.length !== 0 && !response.headersList.get('Sec-WebSocket-Protocol')) { + failWebsocketConnection(ws, 'Server did not respond with sent protocols.') + return + } + + // 3. Follow the requirements stated step 2 to step 6, inclusive, + // of the last set of steps in section 4.1 of The WebSocket + // Protocol to validate response. This either results in fail + // the WebSocket connection or the WebSocket connection is + // established. + + // 2. If the response lacks an |Upgrade| header field or the |Upgrade| + // header field contains a value that is not an ASCII case- + // insensitive match for the value "websocket", the client MUST + // _Fail the WebSocket Connection_. + if (response.headersList.get('Upgrade')?.toLowerCase() !== 'websocket') { + failWebsocketConnection(ws, 'Server did not set Upgrade header to "websocket".') + return + } + + // 3. If the response lacks a |Connection| header field or the + // |Connection| header field doesn't contain a token that is an + // ASCII case-insensitive match for the value "Upgrade", the client + // MUST _Fail the WebSocket Connection_. + if (response.headersList.get('Connection')?.toLowerCase() !== 'upgrade') { + failWebsocketConnection(ws, 'Server did not set Connection header to "upgrade".') + return + } + + // 4. If the response lacks a |Sec-WebSocket-Accept| header field or + // the |Sec-WebSocket-Accept| contains a value other than the + // base64-encoded SHA-1 of the concatenation of the |Sec-WebSocket- + // Key| (as a string, not base64-decoded) with the string "258EAFA5- + // E914-47DA-95CA-C5AB0DC85B11" but ignoring any leading and + // trailing whitespace, the client MUST _Fail the WebSocket + // Connection_. + const secWSAccept = response.headersList.get('Sec-WebSocket-Accept') + const digest = crypto.createHash('sha1').update(keyValue + uid).digest('base64') + if (secWSAccept !== digest) { + failWebsocketConnection(ws, 'Incorrect hash received in Sec-WebSocket-Accept header.') + return + } + + // 5. If the response includes a |Sec-WebSocket-Extensions| header + // field and this header field indicates the use of an extension + // that was not present in the client's handshake (the server has + // indicated an extension not requested by the client), the client + // MUST _Fail the WebSocket Connection_. (The parsing of this + // header field to determine which extensions are requested is + // discussed in Section 9.1.) + const secExtension = response.headersList.get('Sec-WebSocket-Extensions') + + if (secExtension !== null && secExtension !== permessageDeflate) { + failWebsocketConnection(ws, 'Received different permessage-deflate than the one set.') + return + } + + // 6. If the response includes a |Sec-WebSocket-Protocol| header field + // and this header field indicates the use of a subprotocol that was + // not present in the client's handshake (the server has indicated a + // subprotocol not requested by the client), the client MUST _Fail + // the WebSocket Connection_. + const secProtocol = response.headersList.get('Sec-WebSocket-Protocol') + + if (secProtocol !== null && secProtocol !== request.headersList.get('Sec-WebSocket-Protocol')) { + failWebsocketConnection(ws, 'Protocol was not set in the opening handshake.') + return + } + + response.socket.on('data', onSocketData) + response.socket.on('close', onSocketClose) + response.socket.on('error', onSocketError) + + if (channels.open.hasSubscribers) { + channels.open.publish({ + address: response.socket.address(), + protocol: secProtocol, + extensions: secExtension + }) + } + + onEstablish(response) + } + }) + + return controller +} + +/** + * @param {Buffer} chunk + */ +function onSocketData (chunk) { + if (!this.ws[kByteParser].write(chunk)) { + this.pause() + } +} + +/** + * @see https://websockets.spec.whatwg.org/#feedback-from-the-protocol + * @see https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.4 + */ +function onSocketClose () { + const { ws } = this + + // If the TCP connection was closed after the + // WebSocket closing handshake was completed, the WebSocket connection + // is said to have been closed _cleanly_. + const wasClean = ws[kSentClose] && ws[kReceivedClose] + + let code = 1005 + let reason = '' + + const result = ws[kByteParser].closingInfo + + if (result) { + code = result.code ?? 1005 + reason = result.reason + } else if (!ws[kSentClose]) { + // If _The WebSocket + // Connection is Closed_ and no Close control frame was received by the + // endpoint (such as could occur if the underlying transport connection + // is lost), _The WebSocket Connection Close Code_ is considered to be + // 1006. + code = 1006 + } + + // 1. Change the ready state to CLOSED (3). + ws[kReadyState] = states.CLOSED + + // 2. If the user agent was required to fail the WebSocket + // connection, or if the WebSocket connection was closed + // after being flagged as full, fire an event named error + // at the WebSocket object. + // TODO + + // 3. Fire an event named close at the WebSocket object, + // using CloseEvent, with the wasClean attribute + // initialized to true if the connection closed cleanly + // and false otherwise, the code attribute initialized to + // the WebSocket connection close code, and the reason + // attribute initialized to the result of applying UTF-8 + // decode without BOM to the WebSocket connection close + // reason. + fireEvent('close', ws, CloseEvent, { + wasClean, code, reason + }) + + if (channels.close.hasSubscribers) { + channels.close.publish({ + websocket: ws, + code, + reason + }) + } +} + +function onSocketError (error) { + const { ws } = this + + ws[kReadyState] = states.CLOSING + + if (channels.socketError.hasSubscribers) { + channels.socketError.publish(error) + } + + this.destroy() +} + +module.exports = { + establishWebSocketConnection +} + + +/***/ }), + +/***/ 9188: +/***/ ((module) => { + +"use strict"; + + +// This is a Globally Unique Identifier unique used +// to validate that the endpoint accepts websocket +// connections. +// See https://www.rfc-editor.org/rfc/rfc6455.html#section-1.3 +const uid = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' + +/** @type {PropertyDescriptor} */ +const staticPropertyDescriptors = { + enumerable: true, + writable: false, + configurable: false +} + +const states = { + CONNECTING: 0, + OPEN: 1, + CLOSING: 2, + CLOSED: 3 +} + +const opcodes = { + CONTINUATION: 0x0, + TEXT: 0x1, + BINARY: 0x2, + CLOSE: 0x8, + PING: 0x9, + PONG: 0xA +} + +const maxUnsigned16Bit = 2 ** 16 - 1 // 65535 + +const parserStates = { + INFO: 0, + PAYLOADLENGTH_16: 2, + PAYLOADLENGTH_64: 3, + READ_DATA: 4 +} + +const emptyBuffer = Buffer.allocUnsafe(0) + +module.exports = { + uid, + staticPropertyDescriptors, + states, + opcodes, + maxUnsigned16Bit, + parserStates, + emptyBuffer +} + + +/***/ }), + +/***/ 2611: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { webidl } = __nccwpck_require__(1744) +const { kEnumerableProperty } = __nccwpck_require__(3983) +const { MessagePort } = __nccwpck_require__(1267) + +/** + * @see https://html.spec.whatwg.org/multipage/comms.html#messageevent + */ +class MessageEvent extends Event { + #eventInit + + constructor (type, eventInitDict = {}) { + webidl.argumentLengthCheck(arguments, 1, { header: 'MessageEvent constructor' }) + + type = webidl.converters.DOMString(type) + eventInitDict = webidl.converters.MessageEventInit(eventInitDict) + + super(type, eventInitDict) + + this.#eventInit = eventInitDict + } + + get data () { + webidl.brandCheck(this, MessageEvent) + + return this.#eventInit.data + } + + get origin () { + webidl.brandCheck(this, MessageEvent) + + return this.#eventInit.origin + } + + get lastEventId () { + webidl.brandCheck(this, MessageEvent) + + return this.#eventInit.lastEventId + } + + get source () { + webidl.brandCheck(this, MessageEvent) + + return this.#eventInit.source + } + + get ports () { + webidl.brandCheck(this, MessageEvent) + + if (!Object.isFrozen(this.#eventInit.ports)) { + Object.freeze(this.#eventInit.ports) + } + + return this.#eventInit.ports + } + + initMessageEvent ( + type, + bubbles = false, + cancelable = false, + data = null, + origin = '', + lastEventId = '', + source = null, + ports = [] + ) { + webidl.brandCheck(this, MessageEvent) + + webidl.argumentLengthCheck(arguments, 1, { header: 'MessageEvent.initMessageEvent' }) + + return new MessageEvent(type, { + bubbles, cancelable, data, origin, lastEventId, source, ports + }) + } +} + +/** + * @see https://websockets.spec.whatwg.org/#the-closeevent-interface + */ +class CloseEvent extends Event { + #eventInit + + constructor (type, eventInitDict = {}) { + webidl.argumentLengthCheck(arguments, 1, { header: 'CloseEvent constructor' }) + + type = webidl.converters.DOMString(type) + eventInitDict = webidl.converters.CloseEventInit(eventInitDict) + + super(type, eventInitDict) + + this.#eventInit = eventInitDict + } + + get wasClean () { + webidl.brandCheck(this, CloseEvent) + + return this.#eventInit.wasClean + } + + get code () { + webidl.brandCheck(this, CloseEvent) + + return this.#eventInit.code + } + + get reason () { + webidl.brandCheck(this, CloseEvent) + + return this.#eventInit.reason + } +} + +// https://html.spec.whatwg.org/multipage/webappapis.html#the-errorevent-interface +class ErrorEvent extends Event { + #eventInit + + constructor (type, eventInitDict) { + webidl.argumentLengthCheck(arguments, 1, { header: 'ErrorEvent constructor' }) + + super(type, eventInitDict) + + type = webidl.converters.DOMString(type) + eventInitDict = webidl.converters.ErrorEventInit(eventInitDict ?? {}) + + this.#eventInit = eventInitDict + } + + get message () { + webidl.brandCheck(this, ErrorEvent) + + return this.#eventInit.message + } + + get filename () { + webidl.brandCheck(this, ErrorEvent) + + return this.#eventInit.filename + } + + get lineno () { + webidl.brandCheck(this, ErrorEvent) + + return this.#eventInit.lineno + } + + get colno () { + webidl.brandCheck(this, ErrorEvent) + + return this.#eventInit.colno + } + + get error () { + webidl.brandCheck(this, ErrorEvent) + + return this.#eventInit.error + } +} + +Object.defineProperties(MessageEvent.prototype, { + [Symbol.toStringTag]: { + value: 'MessageEvent', + configurable: true + }, + data: kEnumerableProperty, + origin: kEnumerableProperty, + lastEventId: kEnumerableProperty, + source: kEnumerableProperty, + ports: kEnumerableProperty, + initMessageEvent: kEnumerableProperty +}) + +Object.defineProperties(CloseEvent.prototype, { + [Symbol.toStringTag]: { + value: 'CloseEvent', + configurable: true + }, + reason: kEnumerableProperty, + code: kEnumerableProperty, + wasClean: kEnumerableProperty +}) + +Object.defineProperties(ErrorEvent.prototype, { + [Symbol.toStringTag]: { + value: 'ErrorEvent', + configurable: true + }, + message: kEnumerableProperty, + filename: kEnumerableProperty, + lineno: kEnumerableProperty, + colno: kEnumerableProperty, + error: kEnumerableProperty +}) + +webidl.converters.MessagePort = webidl.interfaceConverter(MessagePort) + +webidl.converters['sequence'] = webidl.sequenceConverter( + webidl.converters.MessagePort +) + +const eventInit = [ + { + key: 'bubbles', + converter: webidl.converters.boolean, + defaultValue: false + }, + { + key: 'cancelable', + converter: webidl.converters.boolean, + defaultValue: false + }, + { + key: 'composed', + converter: webidl.converters.boolean, + defaultValue: false + } +] + +webidl.converters.MessageEventInit = webidl.dictionaryConverter([ + ...eventInit, + { + key: 'data', + converter: webidl.converters.any, + defaultValue: null + }, + { + key: 'origin', + converter: webidl.converters.USVString, + defaultValue: '' + }, + { + key: 'lastEventId', + converter: webidl.converters.DOMString, + defaultValue: '' + }, + { + key: 'source', + // Node doesn't implement WindowProxy or ServiceWorker, so the only + // valid value for source is a MessagePort. + converter: webidl.nullableConverter(webidl.converters.MessagePort), + defaultValue: null + }, + { + key: 'ports', + converter: webidl.converters['sequence'], + get defaultValue () { + return [] + } + } +]) + +webidl.converters.CloseEventInit = webidl.dictionaryConverter([ + ...eventInit, + { + key: 'wasClean', + converter: webidl.converters.boolean, + defaultValue: false + }, + { + key: 'code', + converter: webidl.converters['unsigned short'], + defaultValue: 0 + }, + { + key: 'reason', + converter: webidl.converters.USVString, + defaultValue: '' + } +]) + +webidl.converters.ErrorEventInit = webidl.dictionaryConverter([ + ...eventInit, + { + key: 'message', + converter: webidl.converters.DOMString, + defaultValue: '' + }, + { + key: 'filename', + converter: webidl.converters.USVString, + defaultValue: '' + }, + { + key: 'lineno', + converter: webidl.converters['unsigned long'], + defaultValue: 0 + }, + { + key: 'colno', + converter: webidl.converters['unsigned long'], + defaultValue: 0 + }, + { + key: 'error', + converter: webidl.converters.any + } +]) + +module.exports = { + MessageEvent, + CloseEvent, + ErrorEvent +} + + +/***/ }), + +/***/ 5444: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { maxUnsigned16Bit } = __nccwpck_require__(9188) + +/** @type {import('crypto')} */ +let crypto +try { + crypto = __nccwpck_require__(6113) +} catch { + +} + +class WebsocketFrameSend { + /** + * @param {Buffer|undefined} data + */ + constructor (data) { + this.frameData = data + this.maskKey = crypto.randomBytes(4) + } + + createFrame (opcode) { + const bodyLength = this.frameData?.byteLength ?? 0 + + /** @type {number} */ + let payloadLength = bodyLength // 0-125 + let offset = 6 + + if (bodyLength > maxUnsigned16Bit) { + offset += 8 // payload length is next 8 bytes + payloadLength = 127 + } else if (bodyLength > 125) { + offset += 2 // payload length is next 2 bytes + payloadLength = 126 + } + + const buffer = Buffer.allocUnsafe(bodyLength + offset) + + // Clear first 2 bytes, everything else is overwritten + buffer[0] = buffer[1] = 0 + buffer[0] |= 0x80 // FIN + buffer[0] = (buffer[0] & 0xF0) + opcode // opcode + + /*! ws. MIT License. Einar Otto Stangvik */ + buffer[offset - 4] = this.maskKey[0] + buffer[offset - 3] = this.maskKey[1] + buffer[offset - 2] = this.maskKey[2] + buffer[offset - 1] = this.maskKey[3] + + buffer[1] = payloadLength + + if (payloadLength === 126) { + buffer.writeUInt16BE(bodyLength, 2) + } else if (payloadLength === 127) { + // Clear extended payload length + buffer[2] = buffer[3] = 0 + buffer.writeUIntBE(bodyLength, 4, 6) + } + + buffer[1] |= 0x80 // MASK + + // mask body + for (let i = 0; i < bodyLength; i++) { + buffer[offset + i] = this.frameData[i] ^ this.maskKey[i % 4] + } + + return buffer + } +} + +module.exports = { + WebsocketFrameSend +} + + +/***/ }), + +/***/ 1688: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { Writable } = __nccwpck_require__(2781) +const diagnosticsChannel = __nccwpck_require__(7643) +const { parserStates, opcodes, states, emptyBuffer } = __nccwpck_require__(9188) +const { kReadyState, kSentClose, kResponse, kReceivedClose } = __nccwpck_require__(7578) +const { isValidStatusCode, failWebsocketConnection, websocketMessageReceived } = __nccwpck_require__(5515) +const { WebsocketFrameSend } = __nccwpck_require__(5444) + +// This code was influenced by ws released under the MIT license. +// Copyright (c) 2011 Einar Otto Stangvik +// Copyright (c) 2013 Arnout Kazemier and contributors +// Copyright (c) 2016 Luigi Pinca and contributors + +const channels = {} +channels.ping = diagnosticsChannel.channel('undici:websocket:ping') +channels.pong = diagnosticsChannel.channel('undici:websocket:pong') + +class ByteParser extends Writable { + #buffers = [] + #byteOffset = 0 + + #state = parserStates.INFO + + #info = {} + #fragments = [] + + constructor (ws) { + super() + + this.ws = ws + } + + /** + * @param {Buffer} chunk + * @param {() => void} callback + */ + _write (chunk, _, callback) { + this.#buffers.push(chunk) + this.#byteOffset += chunk.length + + this.run(callback) + } + + /** + * Runs whenever a new chunk is received. + * Callback is called whenever there are no more chunks buffering, + * or not enough bytes are buffered to parse. + */ + run (callback) { + while (true) { + if (this.#state === parserStates.INFO) { + // If there aren't enough bytes to parse the payload length, etc. + if (this.#byteOffset < 2) { + return callback() + } + + const buffer = this.consume(2) + + this.#info.fin = (buffer[0] & 0x80) !== 0 + this.#info.opcode = buffer[0] & 0x0F + + // If we receive a fragmented message, we use the type of the first + // frame to parse the full message as binary/text, when it's terminated + this.#info.originalOpcode ??= this.#info.opcode + + this.#info.fragmented = !this.#info.fin && this.#info.opcode !== opcodes.CONTINUATION + + if (this.#info.fragmented && this.#info.opcode !== opcodes.BINARY && this.#info.opcode !== opcodes.TEXT) { + // Only text and binary frames can be fragmented + failWebsocketConnection(this.ws, 'Invalid frame type was fragmented.') + return + } + + const payloadLength = buffer[1] & 0x7F + + if (payloadLength <= 125) { + this.#info.payloadLength = payloadLength + this.#state = parserStates.READ_DATA + } else if (payloadLength === 126) { + this.#state = parserStates.PAYLOADLENGTH_16 + } else if (payloadLength === 127) { + this.#state = parserStates.PAYLOADLENGTH_64 + } + + if (this.#info.fragmented && payloadLength > 125) { + // A fragmented frame can't be fragmented itself + failWebsocketConnection(this.ws, 'Fragmented frame exceeded 125 bytes.') + return + } else if ( + (this.#info.opcode === opcodes.PING || + this.#info.opcode === opcodes.PONG || + this.#info.opcode === opcodes.CLOSE) && + payloadLength > 125 + ) { + // Control frames can have a payload length of 125 bytes MAX + failWebsocketConnection(this.ws, 'Payload length for control frame exceeded 125 bytes.') + return + } else if (this.#info.opcode === opcodes.CLOSE) { + if (payloadLength === 1) { + failWebsocketConnection(this.ws, 'Received close frame with a 1-byte body.') + return + } + + const body = this.consume(payloadLength) + + this.#info.closeInfo = this.parseCloseBody(false, body) + + if (!this.ws[kSentClose]) { + // If an endpoint receives a Close frame and did not previously send a + // Close frame, the endpoint MUST send a Close frame in response. (When + // sending a Close frame in response, the endpoint typically echos the + // status code it received.) + const body = Buffer.allocUnsafe(2) + body.writeUInt16BE(this.#info.closeInfo.code, 0) + const closeFrame = new WebsocketFrameSend(body) + + this.ws[kResponse].socket.write( + closeFrame.createFrame(opcodes.CLOSE), + (err) => { + if (!err) { + this.ws[kSentClose] = true + } + } + ) + } + + // Upon either sending or receiving a Close control frame, it is said + // that _The WebSocket Closing Handshake is Started_ and that the + // WebSocket connection is in the CLOSING state. + this.ws[kReadyState] = states.CLOSING + this.ws[kReceivedClose] = true + + this.end() + + return + } else if (this.#info.opcode === opcodes.PING) { + // Upon receipt of a Ping frame, an endpoint MUST send a Pong frame in + // response, unless it already received a Close frame. + // A Pong frame sent in response to a Ping frame must have identical + // "Application data" + + const body = this.consume(payloadLength) + + if (!this.ws[kReceivedClose]) { + const frame = new WebsocketFrameSend(body) + + this.ws[kResponse].socket.write(frame.createFrame(opcodes.PONG)) + + if (channels.ping.hasSubscribers) { + channels.ping.publish({ + payload: body + }) + } + } + + this.#state = parserStates.INFO + + if (this.#byteOffset > 0) { + continue + } else { + callback() + return + } + } else if (this.#info.opcode === opcodes.PONG) { + // A Pong frame MAY be sent unsolicited. This serves as a + // unidirectional heartbeat. A response to an unsolicited Pong frame is + // not expected. + + const body = this.consume(payloadLength) + + if (channels.pong.hasSubscribers) { + channels.pong.publish({ + payload: body + }) + } + + if (this.#byteOffset > 0) { + continue + } else { + callback() + return + } + } + } else if (this.#state === parserStates.PAYLOADLENGTH_16) { + if (this.#byteOffset < 2) { + return callback() + } + + const buffer = this.consume(2) + + this.#info.payloadLength = buffer.readUInt16BE(0) + this.#state = parserStates.READ_DATA + } else if (this.#state === parserStates.PAYLOADLENGTH_64) { + if (this.#byteOffset < 8) { + return callback() + } + + const buffer = this.consume(8) + const upper = buffer.readUInt32BE(0) + + // 2^31 is the maxinimum bytes an arraybuffer can contain + // on 32-bit systems. Although, on 64-bit systems, this is + // 2^53-1 bytes. + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Invalid_array_length + // https://source.chromium.org/chromium/chromium/src/+/main:v8/src/common/globals.h;drc=1946212ac0100668f14eb9e2843bdd846e510a1e;bpv=1;bpt=1;l=1275 + // https://source.chromium.org/chromium/chromium/src/+/main:v8/src/objects/js-array-buffer.h;l=34;drc=1946212ac0100668f14eb9e2843bdd846e510a1e + if (upper > 2 ** 31 - 1) { + failWebsocketConnection(this.ws, 'Received payload length > 2^31 bytes.') + return + } + + const lower = buffer.readUInt32BE(4) + + this.#info.payloadLength = (upper << 8) + lower + this.#state = parserStates.READ_DATA + } else if (this.#state === parserStates.READ_DATA) { + if (this.#byteOffset < this.#info.payloadLength) { + // If there is still more data in this chunk that needs to be read + return callback() + } else if (this.#byteOffset >= this.#info.payloadLength) { + // If the server sent multiple frames in a single chunk + + const body = this.consume(this.#info.payloadLength) + + this.#fragments.push(body) + + // If the frame is unfragmented, or a fragmented frame was terminated, + // a message was received + if (!this.#info.fragmented || (this.#info.fin && this.#info.opcode === opcodes.CONTINUATION)) { + const fullMessage = Buffer.concat(this.#fragments) + + websocketMessageReceived(this.ws, this.#info.originalOpcode, fullMessage) + + this.#info = {} + this.#fragments.length = 0 + } + + this.#state = parserStates.INFO + } + } + + if (this.#byteOffset > 0) { + continue + } else { + callback() + break + } + } + } + + /** + * Take n bytes from the buffered Buffers + * @param {number} n + * @returns {Buffer|null} + */ + consume (n) { + if (n > this.#byteOffset) { + return null + } else if (n === 0) { + return emptyBuffer + } + + if (this.#buffers[0].length === n) { + this.#byteOffset -= this.#buffers[0].length + return this.#buffers.shift() + } + + const buffer = Buffer.allocUnsafe(n) + let offset = 0 + + while (offset !== n) { + const next = this.#buffers[0] + const { length } = next + + if (length + offset === n) { + buffer.set(this.#buffers.shift(), offset) + break + } else if (length + offset > n) { + buffer.set(next.subarray(0, n - offset), offset) + this.#buffers[0] = next.subarray(n - offset) + break + } else { + buffer.set(this.#buffers.shift(), offset) + offset += next.length + } + } + + this.#byteOffset -= n + + return buffer + } + + parseCloseBody (onlyCode, data) { + // https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.5 + /** @type {number|undefined} */ + let code + + if (data.length >= 2) { + // _The WebSocket Connection Close Code_ is + // defined as the status code (Section 7.4) contained in the first Close + // control frame received by the application + code = data.readUInt16BE(0) + } + + if (onlyCode) { + if (!isValidStatusCode(code)) { + return null + } + + return { code } + } + + // https://datatracker.ietf.org/doc/html/rfc6455#section-7.1.6 + /** @type {Buffer} */ + let reason = data.subarray(2) + + // Remove BOM + if (reason[0] === 0xEF && reason[1] === 0xBB && reason[2] === 0xBF) { + reason = reason.subarray(3) + } + + if (code !== undefined && !isValidStatusCode(code)) { + return null + } + + try { + // TODO: optimize this + reason = new TextDecoder('utf-8', { fatal: true }).decode(reason) + } catch { + return null + } + + return { code, reason } + } + + get closingInfo () { + return this.#info.closeInfo + } +} + +module.exports = { + ByteParser +} + + +/***/ }), + +/***/ 7578: +/***/ ((module) => { + +"use strict"; + + +module.exports = { + kWebSocketURL: Symbol('url'), + kReadyState: Symbol('ready state'), + kController: Symbol('controller'), + kResponse: Symbol('response'), + kBinaryType: Symbol('binary type'), + kSentClose: Symbol('sent close'), + kReceivedClose: Symbol('received close'), + kByteParser: Symbol('byte parser') +} + + +/***/ }), + +/***/ 5515: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { kReadyState, kController, kResponse, kBinaryType, kWebSocketURL } = __nccwpck_require__(7578) +const { states, opcodes } = __nccwpck_require__(9188) +const { MessageEvent, ErrorEvent } = __nccwpck_require__(2611) + +/* globals Blob */ + +/** + * @param {import('./websocket').WebSocket} ws + */ +function isEstablished (ws) { + // If the server's response is validated as provided for above, it is + // said that _The WebSocket Connection is Established_ and that the + // WebSocket Connection is in the OPEN state. + return ws[kReadyState] === states.OPEN +} + +/** + * @param {import('./websocket').WebSocket} ws + */ +function isClosing (ws) { + // Upon either sending or receiving a Close control frame, it is said + // that _The WebSocket Closing Handshake is Started_ and that the + // WebSocket connection is in the CLOSING state. + return ws[kReadyState] === states.CLOSING +} + +/** + * @param {import('./websocket').WebSocket} ws + */ +function isClosed (ws) { + return ws[kReadyState] === states.CLOSED +} + +/** + * @see https://dom.spec.whatwg.org/#concept-event-fire + * @param {string} e + * @param {EventTarget} target + * @param {EventInit | undefined} eventInitDict + */ +function fireEvent (e, target, eventConstructor = Event, eventInitDict) { + // 1. If eventConstructor is not given, then let eventConstructor be Event. + + // 2. Let event be the result of creating an event given eventConstructor, + // in the relevant realm of target. + // 3. Initialize event’s type attribute to e. + const event = new eventConstructor(e, eventInitDict) // eslint-disable-line new-cap + + // 4. Initialize any other IDL attributes of event as described in the + // invocation of this algorithm. + + // 5. Return the result of dispatching event at target, with legacy target + // override flag set if set. + target.dispatchEvent(event) +} + +/** + * @see https://websockets.spec.whatwg.org/#feedback-from-the-protocol + * @param {import('./websocket').WebSocket} ws + * @param {number} type Opcode + * @param {Buffer} data application data + */ +function websocketMessageReceived (ws, type, data) { + // 1. If ready state is not OPEN (1), then return. + if (ws[kReadyState] !== states.OPEN) { + return + } + + // 2. Let dataForEvent be determined by switching on type and binary type: + let dataForEvent + + if (type === opcodes.TEXT) { + // -> type indicates that the data is Text + // a new DOMString containing data + try { + dataForEvent = new TextDecoder('utf-8', { fatal: true }).decode(data) + } catch { + failWebsocketConnection(ws, 'Received invalid UTF-8 in text frame.') + return + } + } else if (type === opcodes.BINARY) { + if (ws[kBinaryType] === 'blob') { + // -> type indicates that the data is Binary and binary type is "blob" + // a new Blob object, created in the relevant Realm of the WebSocket + // object, that represents data as its raw data + dataForEvent = new Blob([data]) + } else { + // -> type indicates that the data is Binary and binary type is "arraybuffer" + // a new ArrayBuffer object, created in the relevant Realm of the + // WebSocket object, whose contents are data + dataForEvent = new Uint8Array(data).buffer + } + } + + // 3. Fire an event named message at the WebSocket object, using MessageEvent, + // with the origin attribute initialized to the serialization of the WebSocket + // object’s url's origin, and the data attribute initialized to dataForEvent. + fireEvent('message', ws, MessageEvent, { + origin: ws[kWebSocketURL].origin, + data: dataForEvent + }) +} + +/** + * @see https://datatracker.ietf.org/doc/html/rfc6455 + * @see https://datatracker.ietf.org/doc/html/rfc2616 + * @see https://bugs.chromium.org/p/chromium/issues/detail?id=398407 + * @param {string} protocol + */ +function isValidSubprotocol (protocol) { + // If present, this value indicates one + // or more comma-separated subprotocol the client wishes to speak, + // ordered by preference. The elements that comprise this value + // MUST be non-empty strings with characters in the range U+0021 to + // U+007E not including separator characters as defined in + // [RFC2616] and MUST all be unique strings. + if (protocol.length === 0) { + return false + } + + for (const char of protocol) { + const code = char.charCodeAt(0) + + if ( + code < 0x21 || + code > 0x7E || + char === '(' || + char === ')' || + char === '<' || + char === '>' || + char === '@' || + char === ',' || + char === ';' || + char === ':' || + char === '\\' || + char === '"' || + char === '/' || + char === '[' || + char === ']' || + char === '?' || + char === '=' || + char === '{' || + char === '}' || + code === 32 || // SP + code === 9 // HT + ) { + return false + } + } + + return true +} + +/** + * @see https://datatracker.ietf.org/doc/html/rfc6455#section-7-4 + * @param {number} code + */ +function isValidStatusCode (code) { + if (code >= 1000 && code < 1015) { + return ( + code !== 1004 && // reserved + code !== 1005 && // "MUST NOT be set as a status code" + code !== 1006 // "MUST NOT be set as a status code" + ) + } + + return code >= 3000 && code <= 4999 +} + +/** + * @param {import('./websocket').WebSocket} ws + * @param {string|undefined} reason + */ +function failWebsocketConnection (ws, reason) { + const { [kController]: controller, [kResponse]: response } = ws + + controller.abort() + + if (response?.socket && !response.socket.destroyed) { + response.socket.destroy() + } + + if (reason) { + fireEvent('error', ws, ErrorEvent, { + error: new Error(reason) + }) + } +} + +module.exports = { + isEstablished, + isClosing, + isClosed, + fireEvent, + isValidSubprotocol, + isValidStatusCode, + failWebsocketConnection, + websocketMessageReceived +} + + +/***/ }), + +/***/ 4284: +/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { + +"use strict"; + + +const { webidl } = __nccwpck_require__(1744) +const { DOMException } = __nccwpck_require__(1037) +const { URLSerializer } = __nccwpck_require__(685) +const { getGlobalOrigin } = __nccwpck_require__(1246) +const { staticPropertyDescriptors, states, opcodes, emptyBuffer } = __nccwpck_require__(9188) +const { + kWebSocketURL, + kReadyState, + kController, + kBinaryType, + kResponse, + kSentClose, + kByteParser +} = __nccwpck_require__(7578) +const { isEstablished, isClosing, isValidSubprotocol, failWebsocketConnection, fireEvent } = __nccwpck_require__(5515) +const { establishWebSocketConnection } = __nccwpck_require__(5354) +const { WebsocketFrameSend } = __nccwpck_require__(5444) +const { ByteParser } = __nccwpck_require__(1688) +const { kEnumerableProperty, isBlobLike } = __nccwpck_require__(3983) +const { getGlobalDispatcher } = __nccwpck_require__(1892) +const { types } = __nccwpck_require__(3837) + +let experimentalWarned = false + +// https://websockets.spec.whatwg.org/#interface-definition +class WebSocket extends EventTarget { + #events = { + open: null, + error: null, + close: null, + message: null + } + + #bufferedAmount = 0 + #protocol = '' + #extensions = '' + + /** + * @param {string} url + * @param {string|string[]} protocols + */ + constructor (url, protocols = []) { + super() + + webidl.argumentLengthCheck(arguments, 1, { header: 'WebSocket constructor' }) + + if (!experimentalWarned) { + experimentalWarned = true + process.emitWarning('WebSockets are experimental, expect them to change at any time.', { + code: 'UNDICI-WS' + }) + } + + const options = webidl.converters['DOMString or sequence or WebSocketInit'](protocols) + + url = webidl.converters.USVString(url) + protocols = options.protocols + + // 1. Let baseURL be this's relevant settings object's API base URL. + const baseURL = getGlobalOrigin() + + // 1. Let urlRecord be the result of applying the URL parser to url with baseURL. + let urlRecord + + try { + urlRecord = new URL(url, baseURL) + } catch (e) { + // 3. If urlRecord is failure, then throw a "SyntaxError" DOMException. + throw new DOMException(e, 'SyntaxError') + } + + // 4. If urlRecord’s scheme is "http", then set urlRecord’s scheme to "ws". + if (urlRecord.protocol === 'http:') { + urlRecord.protocol = 'ws:' + } else if (urlRecord.protocol === 'https:') { + // 5. Otherwise, if urlRecord’s scheme is "https", set urlRecord’s scheme to "wss". + urlRecord.protocol = 'wss:' + } + + // 6. If urlRecord’s scheme is not "ws" or "wss", then throw a "SyntaxError" DOMException. + if (urlRecord.protocol !== 'ws:' && urlRecord.protocol !== 'wss:') { + throw new DOMException( + `Expected a ws: or wss: protocol, got ${urlRecord.protocol}`, + 'SyntaxError' + ) + } + + // 7. If urlRecord’s fragment is non-null, then throw a "SyntaxError" + // DOMException. + if (urlRecord.hash || urlRecord.href.endsWith('#')) { + throw new DOMException('Got fragment', 'SyntaxError') + } + + // 8. If protocols is a string, set protocols to a sequence consisting + // of just that string. + if (typeof protocols === 'string') { + protocols = [protocols] + } + + // 9. If any of the values in protocols occur more than once or otherwise + // fail to match the requirements for elements that comprise the value + // of `Sec-WebSocket-Protocol` fields as defined by The WebSocket + // protocol, then throw a "SyntaxError" DOMException. + if (protocols.length !== new Set(protocols.map(p => p.toLowerCase())).size) { + throw new DOMException('Invalid Sec-WebSocket-Protocol value', 'SyntaxError') + } + + if (protocols.length > 0 && !protocols.every(p => isValidSubprotocol(p))) { + throw new DOMException('Invalid Sec-WebSocket-Protocol value', 'SyntaxError') + } + + // 10. Set this's url to urlRecord. + this[kWebSocketURL] = new URL(urlRecord.href) + + // 11. Let client be this's relevant settings object. + + // 12. Run this step in parallel: + + // 1. Establish a WebSocket connection given urlRecord, protocols, + // and client. + this[kController] = establishWebSocketConnection( + urlRecord, + protocols, + this, + (response) => this.#onConnectionEstablished(response), + options + ) + + // Each WebSocket object has an associated ready state, which is a + // number representing the state of the connection. Initially it must + // be CONNECTING (0). + this[kReadyState] = WebSocket.CONNECTING + + // The extensions attribute must initially return the empty string. + + // The protocol attribute must initially return the empty string. + + // Each WebSocket object has an associated binary type, which is a + // BinaryType. Initially it must be "blob". + this[kBinaryType] = 'blob' + } + + /** + * @see https://websockets.spec.whatwg.org/#dom-websocket-close + * @param {number|undefined} code + * @param {string|undefined} reason + */ + close (code = undefined, reason = undefined) { + webidl.brandCheck(this, WebSocket) + + if (code !== undefined) { + code = webidl.converters['unsigned short'](code, { clamp: true }) + } + + if (reason !== undefined) { + reason = webidl.converters.USVString(reason) + } + + // 1. If code is present, but is neither an integer equal to 1000 nor an + // integer in the range 3000 to 4999, inclusive, throw an + // "InvalidAccessError" DOMException. + if (code !== undefined) { + if (code !== 1000 && (code < 3000 || code > 4999)) { + throw new DOMException('invalid code', 'InvalidAccessError') + } + } + + let reasonByteLength = 0 + + // 2. If reason is present, then run these substeps: + if (reason !== undefined) { + // 1. Let reasonBytes be the result of encoding reason. + // 2. If reasonBytes is longer than 123 bytes, then throw a + // "SyntaxError" DOMException. + reasonByteLength = Buffer.byteLength(reason) + + if (reasonByteLength > 123) { + throw new DOMException( + `Reason must be less than 123 bytes; received ${reasonByteLength}`, + 'SyntaxError' + ) + } + } + + // 3. Run the first matching steps from the following list: + if (this[kReadyState] === WebSocket.CLOSING || this[kReadyState] === WebSocket.CLOSED) { + // If this's ready state is CLOSING (2) or CLOSED (3) + // Do nothing. + } else if (!isEstablished(this)) { + // If the WebSocket connection is not yet established + // Fail the WebSocket connection and set this's ready state + // to CLOSING (2). + failWebsocketConnection(this, 'Connection was closed before it was established.') + this[kReadyState] = WebSocket.CLOSING + } else if (!isClosing(this)) { + // If the WebSocket closing handshake has not yet been started + // Start the WebSocket closing handshake and set this's ready + // state to CLOSING (2). + // - If neither code nor reason is present, the WebSocket Close + // message must not have a body. + // - If code is present, then the status code to use in the + // WebSocket Close message must be the integer given by code. + // - If reason is also present, then reasonBytes must be + // provided in the Close message after the status code. + + const frame = new WebsocketFrameSend() + + // If neither code nor reason is present, the WebSocket Close + // message must not have a body. + + // If code is present, then the status code to use in the + // WebSocket Close message must be the integer given by code. + if (code !== undefined && reason === undefined) { + frame.frameData = Buffer.allocUnsafe(2) + frame.frameData.writeUInt16BE(code, 0) + } else if (code !== undefined && reason !== undefined) { + // If reason is also present, then reasonBytes must be + // provided in the Close message after the status code. + frame.frameData = Buffer.allocUnsafe(2 + reasonByteLength) + frame.frameData.writeUInt16BE(code, 0) + // the body MAY contain UTF-8-encoded data with value /reason/ + frame.frameData.write(reason, 2, 'utf-8') + } else { + frame.frameData = emptyBuffer + } + + /** @type {import('stream').Duplex} */ + const socket = this[kResponse].socket + + socket.write(frame.createFrame(opcodes.CLOSE), (err) => { + if (!err) { + this[kSentClose] = true + } + }) + + // Upon either sending or receiving a Close control frame, it is said + // that _The WebSocket Closing Handshake is Started_ and that the + // WebSocket connection is in the CLOSING state. + this[kReadyState] = states.CLOSING + } else { + // Otherwise + // Set this's ready state to CLOSING (2). + this[kReadyState] = WebSocket.CLOSING + } + } + + /** + * @see https://websockets.spec.whatwg.org/#dom-websocket-send + * @param {NodeJS.TypedArray|ArrayBuffer|Blob|string} data + */ + send (data) { + webidl.brandCheck(this, WebSocket) + + webidl.argumentLengthCheck(arguments, 1, { header: 'WebSocket.send' }) + + data = webidl.converters.WebSocketSendData(data) + + // 1. If this's ready state is CONNECTING, then throw an + // "InvalidStateError" DOMException. + if (this[kReadyState] === WebSocket.CONNECTING) { + throw new DOMException('Sent before connected.', 'InvalidStateError') + } + + // 2. Run the appropriate set of steps from the following list: + // https://datatracker.ietf.org/doc/html/rfc6455#section-6.1 + // https://datatracker.ietf.org/doc/html/rfc6455#section-5.2 + + if (!isEstablished(this) || isClosing(this)) { + return + } + + /** @type {import('stream').Duplex} */ + const socket = this[kResponse].socket + + // If data is a string + if (typeof data === 'string') { + // If the WebSocket connection is established and the WebSocket + // closing handshake has not yet started, then the user agent + // must send a WebSocket Message comprised of the data argument + // using a text frame opcode; if the data cannot be sent, e.g. + // because it would need to be buffered but the buffer is full, + // the user agent must flag the WebSocket as full and then close + // the WebSocket connection. Any invocation of this method with a + // string argument that does not throw an exception must increase + // the bufferedAmount attribute by the number of bytes needed to + // express the argument as UTF-8. + + const value = Buffer.from(data) + const frame = new WebsocketFrameSend(value) + const buffer = frame.createFrame(opcodes.TEXT) + + this.#bufferedAmount += value.byteLength + socket.write(buffer, () => { + this.#bufferedAmount -= value.byteLength + }) + } else if (types.isArrayBuffer(data)) { + // If the WebSocket connection is established, and the WebSocket + // closing handshake has not yet started, then the user agent must + // send a WebSocket Message comprised of data using a binary frame + // opcode; if the data cannot be sent, e.g. because it would need + // to be buffered but the buffer is full, the user agent must flag + // the WebSocket as full and then close the WebSocket connection. + // The data to be sent is the data stored in the buffer described + // by the ArrayBuffer object. Any invocation of this method with an + // ArrayBuffer argument that does not throw an exception must + // increase the bufferedAmount attribute by the length of the + // ArrayBuffer in bytes. + + const value = Buffer.from(data) + const frame = new WebsocketFrameSend(value) + const buffer = frame.createFrame(opcodes.BINARY) + + this.#bufferedAmount += value.byteLength + socket.write(buffer, () => { + this.#bufferedAmount -= value.byteLength + }) + } else if (ArrayBuffer.isView(data)) { + // If the WebSocket connection is established, and the WebSocket + // closing handshake has not yet started, then the user agent must + // send a WebSocket Message comprised of data using a binary frame + // opcode; if the data cannot be sent, e.g. because it would need to + // be buffered but the buffer is full, the user agent must flag the + // WebSocket as full and then close the WebSocket connection. The + // data to be sent is the data stored in the section of the buffer + // described by the ArrayBuffer object that data references. Any + // invocation of this method with this kind of argument that does + // not throw an exception must increase the bufferedAmount attribute + // by the length of data’s buffer in bytes. + + const ab = Buffer.from(data, data.byteOffset, data.byteLength) + + const frame = new WebsocketFrameSend(ab) + const buffer = frame.createFrame(opcodes.BINARY) + + this.#bufferedAmount += ab.byteLength + socket.write(buffer, () => { + this.#bufferedAmount -= ab.byteLength + }) + } else if (isBlobLike(data)) { + // If the WebSocket connection is established, and the WebSocket + // closing handshake has not yet started, then the user agent must + // send a WebSocket Message comprised of data using a binary frame + // opcode; if the data cannot be sent, e.g. because it would need to + // be buffered but the buffer is full, the user agent must flag the + // WebSocket as full and then close the WebSocket connection. The data + // to be sent is the raw data represented by the Blob object. Any + // invocation of this method with a Blob argument that does not throw + // an exception must increase the bufferedAmount attribute by the size + // of the Blob object’s raw data, in bytes. + + const frame = new WebsocketFrameSend() + + data.arrayBuffer().then((ab) => { + const value = Buffer.from(ab) + frame.frameData = value + const buffer = frame.createFrame(opcodes.BINARY) + + this.#bufferedAmount += value.byteLength + socket.write(buffer, () => { + this.#bufferedAmount -= value.byteLength + }) + }) + } + } + + get readyState () { + webidl.brandCheck(this, WebSocket) + + // The readyState getter steps are to return this's ready state. + return this[kReadyState] + } + + get bufferedAmount () { + webidl.brandCheck(this, WebSocket) + + return this.#bufferedAmount + } + + get url () { + webidl.brandCheck(this, WebSocket) + + // The url getter steps are to return this's url, serialized. + return URLSerializer(this[kWebSocketURL]) + } + + get extensions () { + webidl.brandCheck(this, WebSocket) + + return this.#extensions + } + + get protocol () { + webidl.brandCheck(this, WebSocket) + + return this.#protocol + } + + get onopen () { + webidl.brandCheck(this, WebSocket) + + return this.#events.open + } + + set onopen (fn) { + webidl.brandCheck(this, WebSocket) + + if (this.#events.open) { + this.removeEventListener('open', this.#events.open) + } + + if (typeof fn === 'function') { + this.#events.open = fn + this.addEventListener('open', fn) + } else { + this.#events.open = null + } + } + + get onerror () { + webidl.brandCheck(this, WebSocket) + + return this.#events.error + } + + set onerror (fn) { + webidl.brandCheck(this, WebSocket) + + if (this.#events.error) { + this.removeEventListener('error', this.#events.error) + } + + if (typeof fn === 'function') { + this.#events.error = fn + this.addEventListener('error', fn) + } else { + this.#events.error = null + } + } + + get onclose () { + webidl.brandCheck(this, WebSocket) + + return this.#events.close + } + + set onclose (fn) { + webidl.brandCheck(this, WebSocket) + + if (this.#events.close) { + this.removeEventListener('close', this.#events.close) + } + + if (typeof fn === 'function') { + this.#events.close = fn + this.addEventListener('close', fn) + } else { + this.#events.close = null + } + } + + get onmessage () { + webidl.brandCheck(this, WebSocket) + + return this.#events.message + } + + set onmessage (fn) { + webidl.brandCheck(this, WebSocket) + + if (this.#events.message) { + this.removeEventListener('message', this.#events.message) + } + + if (typeof fn === 'function') { + this.#events.message = fn + this.addEventListener('message', fn) + } else { + this.#events.message = null + } + } + + get binaryType () { + webidl.brandCheck(this, WebSocket) + + return this[kBinaryType] + } + + set binaryType (type) { + webidl.brandCheck(this, WebSocket) + + if (type !== 'blob' && type !== 'arraybuffer') { + this[kBinaryType] = 'blob' + } else { + this[kBinaryType] = type + } + } + + /** + * @see https://websockets.spec.whatwg.org/#feedback-from-the-protocol + */ + #onConnectionEstablished (response) { + // processResponse is called when the "response’s header list has been received and initialized." + // once this happens, the connection is open + this[kResponse] = response + + const parser = new ByteParser(this) + parser.on('drain', function onParserDrain () { + this.ws[kResponse].socket.resume() + }) + + response.socket.ws = this + this[kByteParser] = parser + + // 1. Change the ready state to OPEN (1). + this[kReadyState] = states.OPEN + + // 2. Change the extensions attribute’s value to the extensions in use, if + // it is not the null value. + // https://datatracker.ietf.org/doc/html/rfc6455#section-9.1 + const extensions = response.headersList.get('sec-websocket-extensions') + + if (extensions !== null) { + this.#extensions = extensions + } + + // 3. Change the protocol attribute’s value to the subprotocol in use, if + // it is not the null value. + // https://datatracker.ietf.org/doc/html/rfc6455#section-1.9 + const protocol = response.headersList.get('sec-websocket-protocol') + + if (protocol !== null) { + this.#protocol = protocol + } + + // 4. Fire an event named open at the WebSocket object. + fireEvent('open', this) + } +} + +// https://websockets.spec.whatwg.org/#dom-websocket-connecting +WebSocket.CONNECTING = WebSocket.prototype.CONNECTING = states.CONNECTING +// https://websockets.spec.whatwg.org/#dom-websocket-open +WebSocket.OPEN = WebSocket.prototype.OPEN = states.OPEN +// https://websockets.spec.whatwg.org/#dom-websocket-closing +WebSocket.CLOSING = WebSocket.prototype.CLOSING = states.CLOSING +// https://websockets.spec.whatwg.org/#dom-websocket-closed +WebSocket.CLOSED = WebSocket.prototype.CLOSED = states.CLOSED + +Object.defineProperties(WebSocket.prototype, { + CONNECTING: staticPropertyDescriptors, + OPEN: staticPropertyDescriptors, + CLOSING: staticPropertyDescriptors, + CLOSED: staticPropertyDescriptors, + url: kEnumerableProperty, + readyState: kEnumerableProperty, + bufferedAmount: kEnumerableProperty, + onopen: kEnumerableProperty, + onerror: kEnumerableProperty, + onclose: kEnumerableProperty, + close: kEnumerableProperty, + onmessage: kEnumerableProperty, + binaryType: kEnumerableProperty, + send: kEnumerableProperty, + extensions: kEnumerableProperty, + protocol: kEnumerableProperty, + [Symbol.toStringTag]: { + value: 'WebSocket', + writable: false, + enumerable: false, + configurable: true + } +}) + +Object.defineProperties(WebSocket, { + CONNECTING: staticPropertyDescriptors, + OPEN: staticPropertyDescriptors, + CLOSING: staticPropertyDescriptors, + CLOSED: staticPropertyDescriptors +}) + +webidl.converters['sequence'] = webidl.sequenceConverter( + webidl.converters.DOMString +) + +webidl.converters['DOMString or sequence'] = function (V) { + if (webidl.util.Type(V) === 'Object' && Symbol.iterator in V) { + return webidl.converters['sequence'](V) + } + + return webidl.converters.DOMString(V) +} + +// This implements the propsal made in https://github.com/whatwg/websockets/issues/42 +webidl.converters.WebSocketInit = webidl.dictionaryConverter([ + { + key: 'protocols', + converter: webidl.converters['DOMString or sequence'], + get defaultValue () { + return [] + } + }, + { + key: 'dispatcher', + converter: (V) => V, + get defaultValue () { + return getGlobalDispatcher() + } + }, + { + key: 'headers', + converter: webidl.nullableConverter(webidl.converters.HeadersInit) + } +]) + +webidl.converters['DOMString or sequence or WebSocketInit'] = function (V) { + if (webidl.util.Type(V) === 'Object' && !(Symbol.iterator in V)) { + return webidl.converters.WebSocketInit(V) + } + + return { protocols: webidl.converters['DOMString or sequence'](V) } +} + +webidl.converters.WebSocketSendData = function (V) { + if (webidl.util.Type(V) === 'Object') { + if (isBlobLike(V)) { + return webidl.converters.Blob(V, { strict: false }) + } + + if (ArrayBuffer.isView(V) || types.isAnyArrayBuffer(V)) { + return webidl.converters.BufferSource(V) + } + } + + return webidl.converters.USVString(V) +} + +module.exports = { + WebSocket +} + + +/***/ }), + +/***/ 5030: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ value: true })); + +function getUserAgent() { + if (typeof navigator === "object" && "userAgent" in navigator) { + return navigator.userAgent; + } + + if (typeof process === "object" && process.version !== undefined) { + return `Node.js/${process.version.substr(1)} (${process.platform}; ${process.arch})`; + } + + return ""; +} + +exports.getUserAgent = getUserAgent; +//# sourceMappingURL=index.js.map + + +/***/ }), + +/***/ 5840: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +Object.defineProperty(exports, "v1", ({ + enumerable: true, + get: function () { + return _v.default; + } +})); +Object.defineProperty(exports, "v3", ({ + enumerable: true, + get: function () { + return _v2.default; + } +})); +Object.defineProperty(exports, "v4", ({ + enumerable: true, + get: function () { + return _v3.default; + } +})); +Object.defineProperty(exports, "v5", ({ + enumerable: true, + get: function () { + return _v4.default; + } +})); +Object.defineProperty(exports, "NIL", ({ + enumerable: true, + get: function () { + return _nil.default; + } +})); +Object.defineProperty(exports, "version", ({ + enumerable: true, + get: function () { + return _version.default; + } +})); +Object.defineProperty(exports, "validate", ({ + enumerable: true, + get: function () { + return _validate.default; + } +})); +Object.defineProperty(exports, "stringify", ({ + enumerable: true, + get: function () { + return _stringify.default; + } +})); +Object.defineProperty(exports, "parse", ({ + enumerable: true, + get: function () { + return _parse.default; + } +})); + +var _v = _interopRequireDefault(__nccwpck_require__(8628)); + +var _v2 = _interopRequireDefault(__nccwpck_require__(6409)); + +var _v3 = _interopRequireDefault(__nccwpck_require__(5122)); + +var _v4 = _interopRequireDefault(__nccwpck_require__(9120)); + +var _nil = _interopRequireDefault(__nccwpck_require__(5332)); + +var _version = _interopRequireDefault(__nccwpck_require__(1595)); + +var _validate = _interopRequireDefault(__nccwpck_require__(6900)); + +var _stringify = _interopRequireDefault(__nccwpck_require__(8950)); + +var _parse = _interopRequireDefault(__nccwpck_require__(2746)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/***/ }), + +/***/ 4569: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _crypto = _interopRequireDefault(__nccwpck_require__(6113)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function md5(bytes) { + if (Array.isArray(bytes)) { + bytes = Buffer.from(bytes); + } else if (typeof bytes === 'string') { + bytes = Buffer.from(bytes, 'utf8'); + } + + return _crypto.default.createHash('md5').update(bytes).digest(); +} + +var _default = md5; +exports["default"] = _default; + +/***/ }), + +/***/ 5332: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _default = '00000000-0000-0000-0000-000000000000'; +exports["default"] = _default; + +/***/ }), + +/***/ 2746: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _validate = _interopRequireDefault(__nccwpck_require__(6900)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function parse(uuid) { + if (!(0, _validate.default)(uuid)) { + throw TypeError('Invalid UUID'); + } + + let v; + const arr = new Uint8Array(16); // Parse ########-....-....-....-............ + + arr[0] = (v = parseInt(uuid.slice(0, 8), 16)) >>> 24; + arr[1] = v >>> 16 & 0xff; + arr[2] = v >>> 8 & 0xff; + arr[3] = v & 0xff; // Parse ........-####-....-....-............ + + arr[4] = (v = parseInt(uuid.slice(9, 13), 16)) >>> 8; + arr[5] = v & 0xff; // Parse ........-....-####-....-............ + + arr[6] = (v = parseInt(uuid.slice(14, 18), 16)) >>> 8; + arr[7] = v & 0xff; // Parse ........-....-....-####-............ + + arr[8] = (v = parseInt(uuid.slice(19, 23), 16)) >>> 8; + arr[9] = v & 0xff; // Parse ........-....-....-....-############ + // (Use "/" to avoid 32-bit truncation when bit-shifting high-order bytes) + + arr[10] = (v = parseInt(uuid.slice(24, 36), 16)) / 0x10000000000 & 0xff; + arr[11] = v / 0x100000000 & 0xff; + arr[12] = v >>> 24 & 0xff; + arr[13] = v >>> 16 & 0xff; + arr[14] = v >>> 8 & 0xff; + arr[15] = v & 0xff; + return arr; +} + +var _default = parse; +exports["default"] = _default; + +/***/ }), + +/***/ 814: +/***/ ((__unused_webpack_module, exports) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; +var _default = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i; +exports["default"] = _default; + +/***/ }), + +/***/ 807: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = rng; + +var _crypto = _interopRequireDefault(__nccwpck_require__(6113)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +const rnds8Pool = new Uint8Array(256); // # of random values to pre-allocate + +let poolPtr = rnds8Pool.length; + +function rng() { + if (poolPtr > rnds8Pool.length - 16) { + _crypto.default.randomFillSync(rnds8Pool); + + poolPtr = 0; + } + + return rnds8Pool.slice(poolPtr, poolPtr += 16); +} + +/***/ }), + +/***/ 5274: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _crypto = _interopRequireDefault(__nccwpck_require__(6113)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function sha1(bytes) { + if (Array.isArray(bytes)) { + bytes = Buffer.from(bytes); + } else if (typeof bytes === 'string') { + bytes = Buffer.from(bytes, 'utf8'); + } + + return _crypto.default.createHash('sha1').update(bytes).digest(); +} + +var _default = sha1; +exports["default"] = _default; + +/***/ }), + +/***/ 8950: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _validate = _interopRequireDefault(__nccwpck_require__(6900)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +/** + * Convert array of 16 byte values to UUID string format of the form: + * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX + */ +const byteToHex = []; + +for (let i = 0; i < 256; ++i) { + byteToHex.push((i + 0x100).toString(16).substr(1)); +} + +function stringify(arr, offset = 0) { + // Note: Be careful editing this code! It's been tuned for performance + // and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434 + const uuid = (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + '-' + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + '-' + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + '-' + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + '-' + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase(); // Consistency check for valid UUID. If this throws, it's likely due to one + // of the following: + // - One or more input array values don't map to a hex octet (leading to + // "undefined" in the uuid) + // - Invalid input values for the RFC `version` or `variant` fields + + if (!(0, _validate.default)(uuid)) { + throw TypeError('Stringified UUID is invalid'); + } + + return uuid; +} + +var _default = stringify; +exports["default"] = _default; + +/***/ }), + +/***/ 8628: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _rng = _interopRequireDefault(__nccwpck_require__(807)); + +var _stringify = _interopRequireDefault(__nccwpck_require__(8950)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +// **`v1()` - Generate time-based UUID** +// +// Inspired by https://github.com/LiosK/UUID.js +// and http://docs.python.org/library/uuid.html +let _nodeId; + +let _clockseq; // Previous uuid creation time + + +let _lastMSecs = 0; +let _lastNSecs = 0; // See https://github.com/uuidjs/uuid for API details + +function v1(options, buf, offset) { + let i = buf && offset || 0; + const b = buf || new Array(16); + options = options || {}; + let node = options.node || _nodeId; + let clockseq = options.clockseq !== undefined ? options.clockseq : _clockseq; // node and clockseq need to be initialized to random values if they're not + // specified. We do this lazily to minimize issues related to insufficient + // system entropy. See #189 + + if (node == null || clockseq == null) { + const seedBytes = options.random || (options.rng || _rng.default)(); + + if (node == null) { + // Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1) + node = _nodeId = [seedBytes[0] | 0x01, seedBytes[1], seedBytes[2], seedBytes[3], seedBytes[4], seedBytes[5]]; + } + + if (clockseq == null) { + // Per 4.2.2, randomize (14 bit) clockseq + clockseq = _clockseq = (seedBytes[6] << 8 | seedBytes[7]) & 0x3fff; + } + } // UUID timestamps are 100 nano-second units since the Gregorian epoch, + // (1582-10-15 00:00). JSNumbers aren't precise enough for this, so + // time is handled internally as 'msecs' (integer milliseconds) and 'nsecs' + // (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00. + + + let msecs = options.msecs !== undefined ? options.msecs : Date.now(); // Per 4.2.1.2, use count of uuid's generated during the current clock + // cycle to simulate higher resolution clock + + let nsecs = options.nsecs !== undefined ? options.nsecs : _lastNSecs + 1; // Time since last uuid creation (in msecs) + + const dt = msecs - _lastMSecs + (nsecs - _lastNSecs) / 10000; // Per 4.2.1.2, Bump clockseq on clock regression + + if (dt < 0 && options.clockseq === undefined) { + clockseq = clockseq + 1 & 0x3fff; + } // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new + // time interval + + + if ((dt < 0 || msecs > _lastMSecs) && options.nsecs === undefined) { + nsecs = 0; + } // Per 4.2.1.2 Throw error if too many uuids are requested + + + if (nsecs >= 10000) { + throw new Error("uuid.v1(): Can't create more than 10M uuids/sec"); + } + + _lastMSecs = msecs; + _lastNSecs = nsecs; + _clockseq = clockseq; // Per 4.1.4 - Convert from unix epoch to Gregorian epoch + + msecs += 12219292800000; // `time_low` + + const tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000; + b[i++] = tl >>> 24 & 0xff; + b[i++] = tl >>> 16 & 0xff; + b[i++] = tl >>> 8 & 0xff; + b[i++] = tl & 0xff; // `time_mid` + + const tmh = msecs / 0x100000000 * 10000 & 0xfffffff; + b[i++] = tmh >>> 8 & 0xff; + b[i++] = tmh & 0xff; // `time_high_and_version` + + b[i++] = tmh >>> 24 & 0xf | 0x10; // include version + + b[i++] = tmh >>> 16 & 0xff; // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant) + + b[i++] = clockseq >>> 8 | 0x80; // `clock_seq_low` + + b[i++] = clockseq & 0xff; // `node` + + for (let n = 0; n < 6; ++n) { + b[i + n] = node[n]; + } + + return buf || (0, _stringify.default)(b); +} + +var _default = v1; +exports["default"] = _default; + +/***/ }), + +/***/ 6409: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _v = _interopRequireDefault(__nccwpck_require__(5998)); + +var _md = _interopRequireDefault(__nccwpck_require__(4569)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +const v3 = (0, _v.default)('v3', 0x30, _md.default); +var _default = v3; +exports["default"] = _default; + +/***/ }), + +/***/ 5998: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = _default; +exports.URL = exports.DNS = void 0; + +var _stringify = _interopRequireDefault(__nccwpck_require__(8950)); + +var _parse = _interopRequireDefault(__nccwpck_require__(2746)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function stringToBytes(str) { + str = unescape(encodeURIComponent(str)); // UTF8 escape + + const bytes = []; + + for (let i = 0; i < str.length; ++i) { + bytes.push(str.charCodeAt(i)); + } + + return bytes; +} + +const DNS = '6ba7b810-9dad-11d1-80b4-00c04fd430c8'; +exports.DNS = DNS; +const URL = '6ba7b811-9dad-11d1-80b4-00c04fd430c8'; +exports.URL = URL; + +function _default(name, version, hashfunc) { + function generateUUID(value, namespace, buf, offset) { + if (typeof value === 'string') { + value = stringToBytes(value); + } + + if (typeof namespace === 'string') { + namespace = (0, _parse.default)(namespace); + } + + if (namespace.length !== 16) { + throw TypeError('Namespace must be array-like (16 iterable integer values, 0-255)'); + } // Compute hash of namespace and value, Per 4.3 + // Future: Use spread syntax when supported on all platforms, e.g. `bytes = + // hashfunc([...namespace, ... value])` + + + let bytes = new Uint8Array(16 + value.length); + bytes.set(namespace); + bytes.set(value, namespace.length); + bytes = hashfunc(bytes); + bytes[6] = bytes[6] & 0x0f | version; + bytes[8] = bytes[8] & 0x3f | 0x80; + + if (buf) { + offset = offset || 0; + + for (let i = 0; i < 16; ++i) { + buf[offset + i] = bytes[i]; + } + + return buf; + } + + return (0, _stringify.default)(bytes); + } // Function#name is not settable on some platforms (#270) + + + try { + generateUUID.name = name; // eslint-disable-next-line no-empty + } catch (err) {} // For CommonJS default export support + + + generateUUID.DNS = DNS; + generateUUID.URL = URL; + return generateUUID; +} + +/***/ }), + +/***/ 5122: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _rng = _interopRequireDefault(__nccwpck_require__(807)); + +var _stringify = _interopRequireDefault(__nccwpck_require__(8950)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function v4(options, buf, offset) { + options = options || {}; + + const rnds = options.random || (options.rng || _rng.default)(); // Per 4.4, set bits for version and `clock_seq_hi_and_reserved` + + + rnds[6] = rnds[6] & 0x0f | 0x40; + rnds[8] = rnds[8] & 0x3f | 0x80; // Copy bytes to buffer, if provided + + if (buf) { + offset = offset || 0; + + for (let i = 0; i < 16; ++i) { + buf[offset + i] = rnds[i]; + } + + return buf; + } + + return (0, _stringify.default)(rnds); +} + +var _default = v4; +exports["default"] = _default; + +/***/ }), + +/***/ 9120: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _v = _interopRequireDefault(__nccwpck_require__(5998)); + +var _sha = _interopRequireDefault(__nccwpck_require__(5274)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +const v5 = (0, _v.default)('v5', 0x50, _sha.default); +var _default = v5; +exports["default"] = _default; + +/***/ }), + +/***/ 6900: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _regex = _interopRequireDefault(__nccwpck_require__(814)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function validate(uuid) { + return typeof uuid === 'string' && _regex.default.test(uuid); +} + +var _default = validate; +exports["default"] = _default; + +/***/ }), + +/***/ 1595: +/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { + +"use strict"; + + +Object.defineProperty(exports, "__esModule", ({ + value: true +})); +exports["default"] = void 0; + +var _validate = _interopRequireDefault(__nccwpck_require__(6900)); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function version(uuid) { + if (!(0, _validate.default)(uuid)) { + throw TypeError('Invalid UUID'); + } + + return parseInt(uuid.substr(14, 1), 16); +} + +var _default = version; +exports["default"] = _default; + +/***/ }), + +/***/ 2940: +/***/ ((module) => { + +// Returns a wrapper function that returns a wrapped callback +// The wrapper function should do some stuff, and return a +// presumably different callback function. +// This makes sure that own properties are retained, so that +// decorations and such are not lost along the way. +module.exports = wrappy +function wrappy (fn, cb) { + if (fn && cb) return wrappy(fn)(cb) + + if (typeof fn !== 'function') + throw new TypeError('need wrapper function') + + Object.keys(fn).forEach(function (k) { + wrapper[k] = fn[k] + }) + + return wrapper + + function wrapper() { + var args = new Array(arguments.length) + for (var i = 0; i < args.length; i++) { + args[i] = arguments[i] + } + var ret = fn.apply(this, args) + var cb = args[args.length-1] + if (typeof ret === 'function' && ret !== cb) { + Object.keys(cb).forEach(function (k) { + ret[k] = cb[k] + }) + } + return ret + } +} + + +/***/ }), + +/***/ 6373: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +const core = __importStar(__nccwpck_require__(2186)); +const github = __importStar(__nccwpck_require__(5438)); +const defaultCloseComment = ` +This issue has been automatically closed because there has been no response +to our request for more information from the original author. With only the +information that is currently in the issue, we don't have enough information +to take action. Please reach out if you have or find the answers we need so +that we can investigate further. +`; +/** + * Reads, interprets, and encapsulates the configuration for the current run of the Action. + */ +class Config { + constructor() { + this.closeComment = this.valueOrDefault(core.getInput('closeComment'), defaultCloseComment); + if (this.closeComment === 'false') { + this.closeComment = undefined; + } + this.daysUntilClose = parseInt(this.valueOrDefault(core.getInput('daysUntilClose'), '14')); + this.repo = github.context.repo; + this.responseRequiredColor = this.valueOrDefault(core.getInput('responseRequiredColor'), 'ffffff'); + this.responseRequiredLabel = this.valueOrDefault(core.getInput('responseRequiredLabel'), 'more-information-needed'); + this.token = core.getInput('token', { required: true }); + } + valueOrDefault(value, defaultValue) { + return value !== '' ? value : defaultValue; + } +} +exports["default"] = Config; + + +/***/ }), + +/***/ 399: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __importDefault = (this && this.__importDefault) || function (mod) { + return (mod && mod.__esModule) ? mod : { "default": mod }; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +const core = __importStar(__nccwpck_require__(2186)); +const config_1 = __importDefault(__nccwpck_require__(6373)); +const no_response_1 = __importDefault(__nccwpck_require__(8908)); +function run() { + return __awaiter(this, void 0, void 0, function* () { + try { + const eventName = process.env['GITHUB_EVENT_NAME']; + const config = new config_1.default(); + const noResponse = new no_response_1.default(config); + if (eventName === 'schedule') { + noResponse.sweep(); + } + else if (eventName === 'issue_comment') { + noResponse.unmark(); + } + else { + core.error(`Unrecognized event: ${eventName}`); + } + } + catch (error) { + core.setFailed(error.message); + } + }); +} +run(); + + +/***/ }), + +/***/ 8908: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +const core = __importStar(__nccwpck_require__(2186)); +const fs = __importStar(__nccwpck_require__(7147)); +const github = __importStar(__nccwpck_require__(5438)); +const scramjet = __importStar(__nccwpck_require__(8750)); +/* eslint-enable */ +const fsp = fs.promises; +class NoResponse { + constructor(config) { + this.config = config; + this.octokit = github.getOctokit(this.config.token); + } + sweep() { + return __awaiter(this, void 0, void 0, function* () { + core.debug('Starting sweep'); + yield this.ensureLabelExists(); + const issues = yield this.getCloseableIssues(); + for (const issue of issues) { + this.close(Object.assign({ issue_number: issue.number }, this.config.repo)); + } + }); + } + unmark() { + var _a, _b; + return __awaiter(this, void 0, void 0, function* () { + core.debug('Starting unmark'); + const { responseRequiredLabel } = this.config; + const payload = yield this.readPayload(); + const owner = payload.repository.owner.login; + const repo = payload.repository.name; + const { number } = payload.issue; + const comment = payload.comment; + const issue = { owner, repo, issue_number: number }; + const issueInfo = yield this.octokit.rest.issues.get(issue); + const isMarked = yield this.hasResponseRequiredLabel(issue); + if (isMarked && ((_a = issueInfo.data.user) === null || _a === void 0 ? void 0 : _a.login) === comment.user.login) { + core.info(`${owner}/${repo}#${number} is being unmarked`); + yield this.octokit.rest.issues.removeLabel({ + owner, + repo, + issue_number: number, + name: responseRequiredLabel + }); + if (issueInfo.data.state === 'closed' && + issueInfo.data.user.login !== ((_b = issueInfo.data.closed_by) === null || _b === void 0 ? void 0 : _b.login)) { + this.octokit.rest.issues.update({ owner, repo, issue_number: number, state: 'open' }); + } + } + }); + } + close(issue) { + return __awaiter(this, void 0, void 0, function* () { + const { closeComment } = this.config; + core.info(`${issue.owner}/${issue.repo}#${issue.issue_number} is being closed`); + if (closeComment) { + yield this.octokit.rest.issues.createComment(Object.assign({ body: closeComment }, issue)); + } + yield this.octokit.rest.issues.update(Object.assign({ state: 'closed' }, issue)); + }); + } + ensureLabelExists() { + return __awaiter(this, void 0, void 0, function* () { + try { + yield this.octokit.rest.issues.getLabel(Object.assign({ name: this.config.responseRequiredLabel }, this.config.repo)); + } + catch (e) { + this.octokit.rest.issues.createLabel(Object.assign({ name: this.config.responseRequiredLabel, color: this.config.responseRequiredColor }, this.config.repo)); + } + }); + } + findLastLabeledEvent(issue) { + return __awaiter(this, void 0, void 0, function* () { + const { responseRequiredLabel } = this.config; + const events = yield this.octokit.paginate((yield this.octokit.rest.issues.listEvents(Object.assign(Object.assign({}, issue), { per_page: 100 })))); + return events + .reverse() + .find((event) => event.event === 'labeled' && event.label.name === responseRequiredLabel); + }); + } + getCloseableIssues() { + return __awaiter(this, void 0, void 0, function* () { + const { owner, repo } = this.config.repo; + const { daysUntilClose, responseRequiredLabel } = this.config; + const q = `repo:${owner}/${repo} is:issue is:open label:"${responseRequiredLabel}"`; + const labeledEarlierThan = this.since(daysUntilClose); + const issues = yield this.octokit.rest.search.issuesAndPullRequests({ + q, + sort: 'updated', + order: 'asc', + per_page: 30 + }); + core.debug(`Issues to check for closing:`); + core.debug(JSON.stringify(issues, null, 2)); + const closableIssues = yield scramjet + .fromArray(issues.data.items) + .filter((issue) => __awaiter(this, void 0, void 0, function* () { + const event = yield this.findLastLabeledEvent(Object.assign({ issue_number: issue.number }, this.config.repo)); + if (!event) { + return false; + } + core.debug(`Checking: ${JSON.stringify(issue, null, 2)}`); + core.debug(`Using: ${JSON.stringify(event, null, 2)}`); + const creationDate = new Date(event.created_at); + core.debug(`${creationDate.toISOString()} < ${labeledEarlierThan.toISOString()} === ${creationDate < labeledEarlierThan}`); + return creationDate < labeledEarlierThan; + })) + .toArray(); + core.debug(`Closeable: ${JSON.stringify(closableIssues, null, 2)}`); + return closableIssues; + }); + } + hasResponseRequiredLabel(issue) { + return __awaiter(this, void 0, void 0, function* () { + const labels = yield this.octokit.rest.issues.listLabelsOnIssue(Object.assign({}, issue)); + return labels.data.map((label) => label.name).includes(this.config.responseRequiredLabel); + }); + } + readPayload() { + return __awaiter(this, void 0, void 0, function* () { + if (!process.env.GITHUB_EVENT_PATH) { + throw new Error('GITHUB_EVENT_PATH is not defined'); + } + const text = (yield fsp.readFile(process.env.GITHUB_EVENT_PATH)).toString(); + return JSON.parse(text); + }); + } + since(days) { + const ttl = days * 24 * 60 * 60 * 1000; + return new Date(new Date().getTime() - ttl); + } +} +exports["default"] = NoResponse; + + +/***/ }), + +/***/ 9491: +/***/ ((module) => { + +"use strict"; +module.exports = require("assert"); + +/***/ }), + +/***/ 852: +/***/ ((module) => { + +"use strict"; +module.exports = require("async_hooks"); + +/***/ }), + +/***/ 4300: +/***/ ((module) => { + +"use strict"; +module.exports = require("buffer"); + +/***/ }), + +/***/ 2081: +/***/ ((module) => { + +"use strict"; +module.exports = require("child_process"); + +/***/ }), + +/***/ 6206: +/***/ ((module) => { + +"use strict"; +module.exports = require("console"); + +/***/ }), + +/***/ 6113: +/***/ ((module) => { + +"use strict"; +module.exports = require("crypto"); + +/***/ }), + +/***/ 7643: +/***/ ((module) => { + +"use strict"; +module.exports = require("diagnostics_channel"); + +/***/ }), + +/***/ 2361: +/***/ ((module) => { + +"use strict"; +module.exports = require("events"); + +/***/ }), + +/***/ 7147: +/***/ ((module) => { + +"use strict"; +module.exports = require("fs"); + +/***/ }), + +/***/ 3685: +/***/ ((module) => { + +"use strict"; +module.exports = require("http"); + +/***/ }), + +/***/ 5158: +/***/ ((module) => { + +"use strict"; +module.exports = require("http2"); + +/***/ }), + +/***/ 5687: +/***/ ((module) => { + +"use strict"; +module.exports = require("https"); + +/***/ }), + +/***/ 1808: +/***/ ((module) => { + +"use strict"; +module.exports = require("net"); + +/***/ }), + +/***/ 5673: +/***/ ((module) => { + +"use strict"; +module.exports = require("node:events"); + +/***/ }), + +/***/ 4492: +/***/ ((module) => { + +"use strict"; +module.exports = require("node:stream"); + +/***/ }), + +/***/ 7261: +/***/ ((module) => { + +"use strict"; +module.exports = require("node:util"); + +/***/ }), + +/***/ 2037: +/***/ ((module) => { + +"use strict"; +module.exports = require("os"); + +/***/ }), + +/***/ 1017: +/***/ ((module) => { + +"use strict"; +module.exports = require("path"); + +/***/ }), + +/***/ 4074: +/***/ ((module) => { + +"use strict"; +module.exports = require("perf_hooks"); + +/***/ }), + +/***/ 3477: +/***/ ((module) => { + +"use strict"; +module.exports = require("querystring"); + +/***/ }), + +/***/ 2781: +/***/ ((module) => { + +"use strict"; +module.exports = require("stream"); + +/***/ }), + +/***/ 5356: +/***/ ((module) => { + +"use strict"; +module.exports = require("stream/web"); + +/***/ }), + +/***/ 1576: +/***/ ((module) => { + +"use strict"; +module.exports = require("string_decoder"); + +/***/ }), + +/***/ 4404: +/***/ ((module) => { + +"use strict"; +module.exports = require("tls"); + +/***/ }), + +/***/ 7310: +/***/ ((module) => { + +"use strict"; +module.exports = require("url"); + +/***/ }), + +/***/ 3837: +/***/ ((module) => { + +"use strict"; +module.exports = require("util"); + +/***/ }), + +/***/ 9830: +/***/ ((module) => { + +"use strict"; +module.exports = require("util/types"); + +/***/ }), + +/***/ 1267: +/***/ ((module) => { + +"use strict"; +module.exports = require("worker_threads"); + +/***/ }), + +/***/ 9796: +/***/ ((module) => { + +"use strict"; +module.exports = require("zlib"); + +/***/ }) + +/******/ }); +/************************************************************************/ +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __nccwpck_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ var threw = true; +/******/ try { +/******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __nccwpck_require__); +/******/ threw = false; +/******/ } finally { +/******/ if(threw) delete __webpack_module_cache__[moduleId]; +/******/ } +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ +/************************************************************************/ +/******/ /* webpack/runtime/compat */ +/******/ +/******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = __dirname + "/"; +/******/ +/************************************************************************/ +/******/ +/******/ // startup +/******/ // Load entry module and return exports +/******/ // This entry module is referenced by other modules so it can't be inlined +/******/ var __webpack_exports__ = __nccwpck_require__(399); +/******/ module.exports = __webpack_exports__; +/******/ +/******/ })() +; \ No newline at end of file diff --git a/format.sh b/format.sh deleted file mode 100755 index 33bd2e7f3..000000000 --- a/format.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -# 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. - -set -e - -dart format --set-exit-if-changed --line-length=120 . diff --git a/gh_actions/third_party/no-response/.eslintignore b/gh_actions/third_party/no-response/.eslintignore deleted file mode 100644 index 218694729..000000000 --- a/gh_actions/third_party/no-response/.eslintignore +++ /dev/null @@ -1,3 +0,0 @@ -dist/ -lib/ -node_modules/ \ No newline at end of file diff --git a/gh_actions/third_party/no-response/.eslintrc.json b/gh_actions/third_party/no-response/.eslintrc.json deleted file mode 100644 index 1205e30e3..000000000 --- a/gh_actions/third_party/no-response/.eslintrc.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "plugins": ["jest", "@typescript-eslint"], - "extends": ["plugin:github/recommended"], - "parser": "@typescript-eslint/parser", - "parserOptions": { - "ecmaVersion": 9, - "sourceType": "module", - "project": "./tsconfig.json" - }, - "rules": { - "eslint-comments/no-use": "off", - "i18n-text/no-en": "off", - "import/no-namespace": "off", - "no-unused-vars": "off", - "@typescript-eslint/no-unused-vars": "error", - "@typescript-eslint/explicit-member-accessibility": ["error", {"accessibility": "no-public"}], - "@typescript-eslint/no-require-imports": "error", - "@typescript-eslint/array-type": "error", - "@typescript-eslint/await-thenable": "error", - "@typescript-eslint/ban-ts-comment": "error", - "camelcase": "off", - "@typescript-eslint/consistent-type-assertions": "error", - "@typescript-eslint/explicit-function-return-type": ["error", {"allowExpressions": true}], - "@typescript-eslint/func-call-spacing": ["error", "never"], - "@typescript-eslint/no-array-constructor": "error", - "@typescript-eslint/no-empty-interface": "error", - "@typescript-eslint/no-explicit-any": "error", - "@typescript-eslint/no-extraneous-class": "error", - "@typescript-eslint/no-for-in-array": "error", - "@typescript-eslint/no-inferrable-types": "error", - "@typescript-eslint/no-misused-new": "error", - "@typescript-eslint/no-namespace": "error", - "@typescript-eslint/no-non-null-assertion": "warn", - "@typescript-eslint/no-unnecessary-qualifier": "error", - "@typescript-eslint/no-unnecessary-type-assertion": "error", - "@typescript-eslint/no-useless-constructor": "error", - "@typescript-eslint/no-var-requires": "error", - "@typescript-eslint/prefer-for-of": "warn", - "@typescript-eslint/prefer-function-type": "warn", - "@typescript-eslint/prefer-includes": "error", - "@typescript-eslint/prefer-string-starts-ends-with": "error", - "@typescript-eslint/promise-function-async": "error", - "@typescript-eslint/require-array-sort-compare": "error", - "@typescript-eslint/restrict-plus-operands": "error", - "semi": "off", - "@typescript-eslint/semi": ["error", "never"], - "@typescript-eslint/type-annotation-spacing": "error", - "@typescript-eslint/unbound-method": "error" - }, - "env": { - "node": true, - "es6": true, - "jest/globals": true - } - } diff --git a/gh_actions/third_party/no-response/.gitattributes b/gh_actions/third_party/no-response/.gitattributes deleted file mode 100644 index af3093739..000000000 --- a/gh_actions/third_party/no-response/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -dist/** -diff linguist-generated=true diff --git a/gh_actions/third_party/no-response/.gitignore b/gh_actions/third_party/no-response/.gitignore deleted file mode 100644 index 6de9a7611..000000000 --- a/gh_actions/third_party/no-response/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -dist/ -lib/ -node_modules/ diff --git a/gh_actions/third_party/no-response/.prettierignore b/gh_actions/third_party/no-response/.prettierignore deleted file mode 100644 index 218694729..000000000 --- a/gh_actions/third_party/no-response/.prettierignore +++ /dev/null @@ -1,3 +0,0 @@ -dist/ -lib/ -node_modules/ \ No newline at end of file diff --git a/gh_actions/third_party/no-response/.prettierrc.yaml b/gh_actions/third_party/no-response/.prettierrc.yaml deleted file mode 100644 index 8f860db46..000000000 --- a/gh_actions/third_party/no-response/.prettierrc.yaml +++ /dev/null @@ -1,4 +0,0 @@ -printWidth: 100 -semi: false -singleQuote: true -trailingComma: none diff --git a/gh_actions/third_party/no-response/LICENSE.md b/gh_actions/third_party/no-response/LICENSE.md deleted file mode 100644 index 15f3bc033..000000000 --- a/gh_actions/third_party/no-response/LICENSE.md +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright © 2021 [Lee Dohm](https://www.lee-dohm.com) - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/gh_actions/third_party/no-response/README.md b/gh_actions/third_party/no-response/README.md deleted file mode 100644 index b830023f0..000000000 --- a/gh_actions/third_party/no-response/README.md +++ /dev/null @@ -1,75 +0,0 @@ -# No Response - -A GitHub Action that closes Issues where the author hasn't responded to a request for more information. - -## Use - -Recommended basic configuration: - -```yaml -name: No Response - -# Both `issue_comment` and `scheduled` event types are required for this Action -# to work properly. -on: - issue_comment: - types: [created] - schedule: - # Schedule for five minutes after the hour, every hour - - cron: '5 * * * *' - -jobs: - noResponse: - runs-on: ubuntu-latest - steps: - - uses: lee-dohm/no-response@v0.5.0 - with: - token: ${{ github.token }} -``` - -### Inputs - -See [`action.yml`](action.yml) for defaults. - -- `closeComment` — Markdown text to post as a comment when an issue is going to be closed. Set to `false` to disable commenting when closing an issue. -- `daysUntilClose` — Number of days to wait for a response from the original author before closing. -- `responseRequiredColor` — Color for the `responseRequiredLabel`. **Only** used when creating the label if it does not already exist. -- `responseRequiredLabel` — Text of the label used to indicate that a response from the original author is required. -- `token` — Token used to access repo information. The default GitHub Actions token is sufficient. - -### Outputs - -None. - -## Action flow - -The intent of this Action is to close issues that have not received a response to a maintainer's request for more information. Many times issues will be filed without enough information to be properly investigated. This Action allows maintainers to label an issue as requiring more information from the original author. If the information is not received in a timely manner, the issue will be closed. If the original author comes back and gives more information, the label is removed and the issue is reopened, if necessary. - -### Scheduled - -At the scheduled times, it searches for issues that are: - -- Open -- Have a label named the same as the `responseRequiredLabel` value in the configuration -- The `responseRequiredLabel` was applied more than `daysUntilClose` ago - -For each issue found, it: - -1. If `closeComment` is not `false`, posts the contents of `closeComment` -1. Closes the issue - -### `issue_comment` Event - -When an `issue_comment` event is received, if all of the following are true: - -- The author of the comment is the original author of the issue -- The issue has a label named the same as the `responseRequiredLabel` value in the configuration - -It will: - -1. Remove the `responseRequiredLabel` -1. Reopen the issue if it was closed by someone other than the original author of the issue - -## License - -[MIT](LICENSE.md) diff --git a/gh_actions/third_party/no-response/jest.config.js b/gh_actions/third_party/no-response/jest.config.js deleted file mode 100644 index 563d4ccb8..000000000 --- a/gh_actions/third_party/no-response/jest.config.js +++ /dev/null @@ -1,11 +0,0 @@ -module.exports = { - clearMocks: true, - moduleFileExtensions: ['js', 'ts'], - testEnvironment: 'node', - testMatch: ['**/*.test.ts'], - testRunner: 'jest-circus/runner', - transform: { - '^.+\\.ts$': 'ts-jest' - }, - verbose: true -} \ No newline at end of file diff --git a/gh_actions/third_party/no-response/package-lock.json b/gh_actions/third_party/no-response/package-lock.json deleted file mode 100644 index 4a6aecb4e..000000000 --- a/gh_actions/third_party/no-response/package-lock.json +++ /dev/null @@ -1,12427 +0,0 @@ -{ - "name": "no-response", - "version": "0.0.0", - "lockfileVersion": 2, - "requires": true, - "packages": { - "": { - "name": "no-response", - "version": "0.0.0", - "license": "MIT", - "dependencies": { - "@actions/core": "^1.10.1", - "@actions/github": "^6.0.0", - "eslint-plugin-prettier": "^5.0.1", - "scramjet": "^4.37.0" - }, - "devDependencies": { - "@octokit/webhooks-types": "^7.3.1", - "@types/jest": "^29.5.8", - "@types/node": "^20.9.0", - "@typescript-eslint/parser": "^6.10.0", - "@vercel/ncc": "^0.38.1", - "eslint": "^8.53.0", - "eslint-plugin-github": "^4.10.1", - "eslint-plugin-jest": "^27.6.0", - "extract-pr-titles": "^1.1.0", - "jest": "^29.7.0", - "js-yaml": "^4.1.0", - "prettier": "3.0.3", - "ts-jest": "^29.1.1", - "typescript": "^5.2.2" - } - }, - "node_modules/@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@actions/core": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.1.tgz", - "integrity": "sha512-3lBR9EDAY+iYIpTnTIXmWcNbX3T2kCkAEQGIQx4NVQ0575nk2k3GRZDTPQG+vVtS2izSLmINlxXf0uLtnrTP+g==", - "dependencies": { - "@actions/http-client": "^2.0.1", - "uuid": "^8.3.2" - } - }, - "node_modules/@actions/github": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@actions/github/-/github-6.0.0.tgz", - "integrity": "sha512-alScpSVnYmjNEXboZjarjukQEzgCRmjMv6Xj47fsdnqGS73bjJNDpiiXmp8jr0UZLdUB6d9jW63IcmddUP+l0g==", - "dependencies": { - "@actions/http-client": "^2.2.0", - "@octokit/core": "^5.0.1", - "@octokit/plugin-paginate-rest": "^9.0.0", - "@octokit/plugin-rest-endpoint-methods": "^10.0.0" - } - }, - "node_modules/@actions/http-client": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.0.tgz", - "integrity": "sha512-q+epW0trjVUUHboliPb4UF9g2msf+w61b32tAkFEwL/IwP0DQWgbCMM0Hbe3e3WXSKz5VcUXbzJQgy8Hkra/Lg==", - "dependencies": { - "tunnel": "^0.0.6", - "undici": "^5.25.4" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/code-frame/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/code-frame/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/code-frame/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.2.tgz", - "integrity": "sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.2.tgz", - "integrity": "sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-module-transforms": "^7.23.0", - "@babel/helpers": "^7.23.2", - "@babel/parser": "^7.23.0", - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", - "dev": true, - "dependencies": { - "@babel/types": "^7.23.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", - "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.15", - "browserslist": "^4.21.9", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", - "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", - "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz", - "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==", - "dev": true, - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", - "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", - "dev": true, - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", - "dev": true, - "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz", - "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.23.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", - "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/js": { - "version": "8.53.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz", - "integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@fastify/busboy": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.0.0.tgz", - "integrity": "sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ==", - "engines": { - "node": ">=14" - } - }, - "node_modules/@github/browserslist-config": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@github/browserslist-config/-/browserslist-config-1.0.0.tgz", - "integrity": "sha512-gIhjdJp/c2beaIWWIlsXdqXVRUz3r2BxBCpfz/F3JXHvSAQ1paMYjLH+maEATtENg+k5eLV7gA+9yPp762ieuw==", - "dev": true - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", - "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==" - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", - "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", - "dev": true, - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@octokit/auth-token": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", - "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", - "engines": { - "node": ">= 18" - } - }, - "node_modules/@octokit/core": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.0.1.tgz", - "integrity": "sha512-lyeeeZyESFo+ffI801SaBKmCfsvarO+dgV8/0gD8u1d87clbEdWsP5yC+dSj3zLhb2eIf5SJrn6vDz9AheETHw==", - "dependencies": { - "@octokit/auth-token": "^4.0.0", - "@octokit/graphql": "^7.0.0", - "@octokit/request": "^8.0.2", - "@octokit/request-error": "^5.0.0", - "@octokit/types": "^12.0.0", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@octokit/endpoint": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.2.tgz", - "integrity": "sha512-qhKW8YLIi+Kmc92FQUFGr++DYtkx/1fBv+Thua6baqnjnOsgBYJDCvWZR1YcINuHGOEQt416WOfE+A/oG60NBQ==", - "dependencies": { - "@octokit/types": "^12.0.0", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@octokit/graphql": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.2.tgz", - "integrity": "sha512-OJ2iGMtj5Tg3s6RaXH22cJcxXRi7Y3EBqbHTBRq+PQAqfaS8f/236fUrWhfSn8P4jovyzqucxme7/vWSSZBX2Q==", - "dependencies": { - "@octokit/request": "^8.0.1", - "@octokit/types": "^12.0.0", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@octokit/openapi-types": { - "version": "19.0.2", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-19.0.2.tgz", - "integrity": "sha512-8li32fUDUeml/ACRp/njCWTsk5t17cfTM1jp9n08pBrqs5cDFJubtjsSnuz56r5Tad6jdEPJld7LxNp9dNcyjQ==" - }, - "node_modules/@octokit/plugin-paginate-rest": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.1.2.tgz", - "integrity": "sha512-euDbNV6fxX6btsCDnZoZM4vw3zO1nj1Z7TskHAulO6mZ9lHoFTpwll6farf+wh31mlBabgU81bBYdflp0GLVAQ==", - "dependencies": { - "@octokit/types": "^12.1.1" - }, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@octokit/core": ">=5" - } - }, - "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.1.2.tgz", - "integrity": "sha512-JztgZ82CY4JNlPTuF0jh4iWuuGpEi5czFCoXyAbMg4F2XyFBbG5DWAKfa3odRvdZww6Df1tQgBKnqpd9X0WF9g==", - "dependencies": { - "@octokit/types": "^12.1.1" - }, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "@octokit/core": ">=5" - } - }, - "node_modules/@octokit/request": { - "version": "8.1.4", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.1.4.tgz", - "integrity": "sha512-M0aaFfpGPEKrg7XoA/gwgRvc9MSXHRO2Ioki1qrPDbl1e9YhjIwVoHE7HIKmv/m3idzldj//xBujcFNqGX6ENA==", - "dependencies": { - "@octokit/endpoint": "^9.0.0", - "@octokit/request-error": "^5.0.0", - "@octokit/types": "^12.0.0", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@octokit/request-error": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.0.1.tgz", - "integrity": "sha512-X7pnyTMV7MgtGmiXBwmO6M5kIPrntOXdyKZLigNfQWSEQzVxR4a4vo49vJjTWX70mPndj8KhfT4Dx+2Ng3vnBQ==", - "dependencies": { - "@octokit/types": "^12.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/@octokit/types": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.1.1.tgz", - "integrity": "sha512-qnJTldJ1NyGT5MTsCg/Zi+y2IFHZ1Jo5+njNCjJ9FcainV7LjuHgmB697kA0g4MjZeDAJsM3B45iqCVsCLVFZg==", - "dependencies": { - "@octokit/openapi-types": "^19.0.2" - } - }, - "node_modules/@octokit/webhooks-types": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/@octokit/webhooks-types/-/webhooks-types-7.3.1.tgz", - "integrity": "sha512-u6355ZsZnHwmxen30SrqnYb1pXieBFkYgkNzt+Ed4Ao5tupN1OErHfzwiV6hq6duGkDAYASbq7/uVJQ69PjLEg==", - "dev": true - }, - "node_modules/@pkgr/utils": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", - "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", - "dependencies": { - "cross-spawn": "^7.0.3", - "fast-glob": "^3.3.0", - "is-glob": "^4.0.3", - "open": "^9.1.0", - "picocolors": "^1.0.0", - "tslib": "^2.6.0" - }, - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", - "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", - "dev": true, - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.4", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.4.tgz", - "integrity": "sha512-mLnSC22IC4vcWiuObSRjrLd9XcBTGf59vUSoq2jkQDJ/QQ8PMI9rSuzE+aEV8karUMbskw07bKYoUJCKTUaygg==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.6.7", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.7.tgz", - "integrity": "sha512-6Sfsq+EaaLrw4RmdFWE9Onp63TOUue71AWb4Gpa6JxzgTYtimbM086WnYTy2U67AofR++QKCo08ZP6pwx8YFHQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.4", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.4.tgz", - "integrity": "sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz", - "integrity": "sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg==", - "dev": true - }, - "node_modules/@types/istanbul-lib-report": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz", - "integrity": "sha512-3BUTyMzbZa2DtDI2BkERNC6jJw2Mr2Y0oGI7mRxYNBPxppbtEK1F66u3bKwU2g+wxwWI7PAoRpJnOY1grJqzHg==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "29.5.8", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.8.tgz", - "integrity": "sha512-fXEFTxMV2Co8ZF5aYFJv+YeA08RTYJfhtN5c9JSv/mFEMe+xxjufCb+PHL+bJcMs/ebPUsBu+UNTEz+ydXrR6g==", - "dev": true, - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "node_modules/@types/node": { - "version": "20.9.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz", - "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==", - "dev": true, - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==", - "dev": true - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true - }, - "node_modules/@types/yargs": { - "version": "17.0.30", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.30.tgz", - "integrity": "sha512-3SJLzYk3yz3EgI9I8OLoH06B3PdXIoU2imrBZzaGqUtUXf5iUNDtmAfCGuQrny1bnmyjh/GM/YNts6WK5jR5Rw==", - "dev": true, - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-13.1.0.tgz", - "integrity": "sha512-gCubfBUZ6KxzoibJ+SCUc/57Ms1jz5NjHe4+dI2krNmU5zCPAphyLJYyTOg06ueIyfj+SaCUqmzun7ImlxDcKg==", - "dev": true - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.10.0.tgz", - "integrity": "sha512-uoLj4g2OTL8rfUQVx2AFO1hp/zja1wABJq77P6IclQs6I/m9GLrm7jCdgzZkvWdDCQf1uEvoa8s8CupsgWQgVg==", - "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.10.0", - "@typescript-eslint/type-utils": "6.10.0", - "@typescript-eslint/utils": "6.10.0", - "@typescript-eslint/visitor-keys": "6.10.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.4", - "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.10.0.tgz", - "integrity": "sha512-+sZwIj+s+io9ozSxIWbNB5873OSdfeBEH/FR0re14WLI6BaKuSOnnwCJ2foUiu8uXf4dRp1UqHP0vrZ1zXGrog==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "6.10.0", - "@typescript-eslint/types": "6.10.0", - "@typescript-eslint/typescript-estree": "6.10.0", - "@typescript-eslint/visitor-keys": "6.10.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.10.0.tgz", - "integrity": "sha512-TN/plV7dzqqC2iPNf1KrxozDgZs53Gfgg5ZHyw8erd6jd5Ta/JIEcdCheXFt9b1NYb93a1wmIIVW/2gLkombDg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.10.0", - "@typescript-eslint/visitor-keys": "6.10.0" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.10.0.tgz", - "integrity": "sha512-wYpPs3hgTFblMYwbYWPT3eZtaDOjbLyIYuqpwuLBBqhLiuvJ+9sEp2gNRJEtR5N/c9G1uTtQQL5AhV0fEPJYcg==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "6.10.0", - "@typescript-eslint/utils": "6.10.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/types": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.10.0.tgz", - "integrity": "sha512-36Fq1PWh9dusgo3vH7qmQAj5/AZqARky1Wi6WpINxB6SkQdY5vQoT2/7rW7uBIsPDcvvGCLi4r10p0OJ7ITAeg==", - "dev": true, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.10.0.tgz", - "integrity": "sha512-ek0Eyuy6P15LJVeghbWhSrBCj/vJpPXXR+EpaRZqou7achUWL8IdYnMSC5WHAeTWswYQuP2hAZgij/bC9fanBg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.10.0", - "@typescript-eslint/visitor-keys": "6.10.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.10.0.tgz", - "integrity": "sha512-v+pJ1/RcVyRc0o4wAGux9x42RHmAjIGzPRo538Z8M1tVx6HOnoQBCX/NoadHQlZeC+QO2yr4nNSFWOoraZCAyg==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.10.0", - "@typescript-eslint/types": "6.10.0", - "@typescript-eslint/typescript-estree": "6.10.0", - "semver": "^7.5.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/utils/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.10.0.tgz", - "integrity": "sha512-xMGluxQIEtOM7bqFCo+rCMh5fqI+ZxV5RUUOa29iVPz1OgCZrtc7rFnz5cLUazlkPKYqX+75iuDq7m0HQ48nCg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "6.10.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" - }, - "node_modules/@vercel/ncc": { - "version": "0.38.1", - "resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.38.1.tgz", - "integrity": "sha512-IBBb+iI2NLu4VQn3Vwldyi2QwaXt5+hTyh58ggAMoCGE6DJmPvwL3KPBWcJl1m9LYPChBLE980Jw+CS4Wokqxw==", - "dev": true, - "bin": { - "ncc": "dist/ncc/cli.js" - } - }, - "node_modules/acorn": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", - "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "dev": true, - "dependencies": { - "dequal": "^2.0.3" - } - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-includes": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", - "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/array.prototype.findlastindex": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", - "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/arraybuffer.prototype.slice": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", - "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-array-buffer": "^3.0.2", - "is-shared-array-buffer": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/ast-types-flow": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", - "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", - "dev": true - }, - "node_modules/asynciterator.prototype": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", - "integrity": "sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.3" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/axe-core": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.0.tgz", - "integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/axobject-query": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", - "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", - "dev": true, - "dependencies": { - "dequal": "^2.0.3" - } - }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "node_modules/before-after-hook": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", - "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" - }, - "node_modules/big-integer": { - "version": "1.6.51", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", - "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/bplist-parser": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", - "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", - "dependencies": { - "big-integer": "^1.6.44" - }, - "engines": { - "node": ">= 5.10.0" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", - "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "caniuse-lite": "^1.0.30001541", - "electron-to-chromium": "^1.4.535", - "node-releases": "^2.0.13", - "update-browserslist-db": "^1.0.13" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "node_modules/bundle-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", - "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", - "dependencies": { - "run-applescript": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001561", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001561.tgz", - "integrity": "sha512-NTt0DNoKe958Q0BE0j0c1V9jbUzhBxHIEJy7asmGrpE0yG63KTV7PLHPnK2E1O9RsQrQ081I3NLuXGS6zht3cw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", - "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", - "dev": true - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/damerau-levenshtein": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", - "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", - "dev": true - }, - "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/dedent": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", - "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", - "dev": true, - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/default-browser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", - "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", - "dependencies": { - "bundle-name": "^3.0.0", - "default-browser-id": "^3.0.0", - "execa": "^7.1.1", - "titleize": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser-id": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", - "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", - "dependencies": { - "bplist-parser": "^0.2.0", - "untildify": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/define-lazy-prop": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", - "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, - "dependencies": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" - }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.4.577", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.577.tgz", - "integrity": "sha512-/5xHPH6f00SxhHw6052r+5S1xO7gHNc89hV7tqlvnStvKbSrDqc/u6AlwPvVWWNj+s4/KL6T6y8ih+nOY0qYNA==", - "dev": true - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-abstract": { - "version": "1.22.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", - "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", - "dev": true, - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.2", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.5", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.2", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.12", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "safe-array-concat": "^1.0.1", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", - "string.prototype.trimstart": "^1.0.7", - "typed-array-buffer": "^1.0.0", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-iterator-helpers": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz", - "integrity": "sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==", - "dev": true, - "dependencies": { - "asynciterator.prototype": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.1", - "es-set-tostringtag": "^2.0.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.2.1", - "globalthis": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "iterator.prototype": "^1.1.2", - "safe-array-concat": "^1.0.1" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", - "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.2", - "has-tostringtag": "^1.0.0", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", - "dev": true, - "dependencies": { - "hasown": "^2.0.0" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-goat": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", - "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/eslint": { - "version": "8.53.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz", - "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==", - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.3", - "@eslint/js": "8.53.0", - "@humanwhocodes/config-array": "^0.11.13", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-config-prettier": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", - "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", - "dev": true, - "bin": { - "eslint-config-prettier": "bin/cli.js" - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", - "dev": true, - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-module-utils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", - "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", - "dev": true, - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-escompat": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-escompat/-/eslint-plugin-escompat-3.4.0.tgz", - "integrity": "sha512-ufTPv8cwCxTNoLnTZBFTQ5SxU2w7E7wiMIS7PSxsgP1eAxFjtSaoZ80LRn64hI8iYziE6kJG6gX/ZCJVxh48Bg==", - "dev": true, - "dependencies": { - "browserslist": "^4.21.0" - }, - "peerDependencies": { - "eslint": ">=5.14.1" - } - }, - "node_modules/eslint-plugin-eslint-comments": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-3.2.0.tgz", - "integrity": "sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^1.0.5", - "ignore": "^5.0.5" - }, - "engines": { - "node": ">=6.5.0" - } - }, - "node_modules/eslint-plugin-filenames": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-filenames/-/eslint-plugin-filenames-1.3.2.tgz", - "integrity": "sha512-tqxJTiEM5a0JmRCUYQmxw23vtTxrb2+a3Q2mMOPhFxvt7ZQQJmdiuMby9B/vUAuVMghyP7oET+nIf6EO6CBd/w==", - "dev": true, - "dependencies": { - "lodash.camelcase": "4.3.0", - "lodash.kebabcase": "4.1.1", - "lodash.snakecase": "4.1.1", - "lodash.upperfirst": "4.3.1" - }, - "peerDependencies": { - "eslint": "*" - } - }, - "node_modules/eslint-plugin-github": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-github/-/eslint-plugin-github-4.10.1.tgz", - "integrity": "sha512-1AqQBockOM+m0ZUpwfjWtX0lWdX5cRi/hwJnSNvXoOmz/Hh+ULH6QFz6ENWueTWjoWpgPv0af3bj+snps6o4og==", - "dev": true, - "dependencies": { - "@github/browserslist-config": "^1.0.0", - "@typescript-eslint/eslint-plugin": "^6.0.0", - "@typescript-eslint/parser": "^6.0.0", - "aria-query": "^5.3.0", - "eslint-config-prettier": ">=8.0.0", - "eslint-plugin-escompat": "^3.3.3", - "eslint-plugin-eslint-comments": "^3.2.0", - "eslint-plugin-filenames": "^1.3.2", - "eslint-plugin-i18n-text": "^1.0.1", - "eslint-plugin-import": "^2.25.2", - "eslint-plugin-jsx-a11y": "^6.7.1", - "eslint-plugin-no-only-tests": "^3.0.0", - "eslint-plugin-prettier": "^5.0.0", - "eslint-rule-documentation": ">=1.0.0", - "jsx-ast-utils": "^3.3.2", - "prettier": "^3.0.0", - "svg-element-attributes": "^1.3.1" - }, - "bin": { - "eslint-ignore-errors": "bin/eslint-ignore-errors.js" - }, - "peerDependencies": { - "eslint": "^8.0.1" - } - }, - "node_modules/eslint-plugin-i18n-text": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-i18n-text/-/eslint-plugin-i18n-text-1.0.1.tgz", - "integrity": "sha512-3G3UetST6rdqhqW9SfcfzNYMpQXS7wNkJvp6dsXnjzGiku6Iu5hl3B0kmk6lIcFPwYjhQIY+tXVRtK9TlGT7RA==", - "dev": true, - "peerDependencies": { - "eslint": ">=5.0.0" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.0.tgz", - "integrity": "sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.7", - "array.prototype.findlastindex": "^1.2.3", - "array.prototype.flat": "^1.3.2", - "array.prototype.flatmap": "^1.3.2", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.8.0", - "hasown": "^2.0.0", - "is-core-module": "^2.13.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.7", - "object.groupby": "^1.0.1", - "object.values": "^1.1.7", - "semver": "^6.3.1", - "tsconfig-paths": "^3.14.2" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-jest": { - "version": "27.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.6.0.tgz", - "integrity": "sha512-MTlusnnDMChbElsszJvrwD1dN3x6nZl//s4JD23BxB6MgR66TZlL064su24xEIS3VACfAoHV1vgyMgPw8nkdng==", - "dev": true, - "dependencies": { - "@typescript-eslint/utils": "^5.10.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@typescript-eslint/eslint-plugin": "^5.0.0 || ^6.0.0", - "eslint": "^7.0.0 || ^8.0.0", - "jest": "*" - }, - "peerDependenciesMeta": { - "@typescript-eslint/eslint-plugin": { - "optional": true - }, - "jest": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-jest/node_modules/@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/eslint-plugin-jest/node_modules/@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", - "dev": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/eslint-plugin-jest/node_modules/@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-jest/node_modules/@typescript-eslint/utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", - "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/eslint-plugin-jest/node_modules/@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/eslint-plugin-jest/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/eslint-plugin-jest/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/eslint-plugin-jest/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint-plugin-jsx-a11y": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.8.0.tgz", - "integrity": "sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.23.2", - "aria-query": "^5.3.0", - "array-includes": "^3.1.7", - "array.prototype.flatmap": "^1.3.2", - "ast-types-flow": "^0.0.8", - "axe-core": "=4.7.0", - "axobject-query": "^3.2.1", - "damerau-levenshtein": "^1.0.8", - "emoji-regex": "^9.2.2", - "es-iterator-helpers": "^1.0.15", - "hasown": "^2.0.0", - "jsx-ast-utils": "^3.3.5", - "language-tags": "^1.0.9", - "minimatch": "^3.1.2", - "object.entries": "^1.1.7", - "object.fromentries": "^2.0.7" - }, - "engines": { - "node": ">=4.0" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" - } - }, - "node_modules/eslint-plugin-no-only-tests": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-no-only-tests/-/eslint-plugin-no-only-tests-3.1.0.tgz", - "integrity": "sha512-Lf4YW/bL6Un1R6A76pRZyE1dl1vr31G/ev8UzIc/geCgFWyrKil8hVjYqWVKGB/UIGmb6Slzs9T0wNezdSVegw==", - "dev": true, - "engines": { - "node": ">=5.0.0" - } - }, - "node_modules/eslint-plugin-prettier": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz", - "integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==", - "dependencies": { - "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.8.5" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/prettier" - }, - "peerDependencies": { - "@types/eslint": ">=8.0.0", - "eslint": ">=8.0.0", - "prettier": ">=3.0.0" - }, - "peerDependenciesMeta": { - "@types/eslint": { - "optional": true - }, - "eslint-config-prettier": { - "optional": true - } - } - }, - "node_modules/eslint-rule-documentation": { - "version": "1.0.23", - "resolved": "https://registry.npmjs.org/eslint-rule-documentation/-/eslint-rule-documentation-1.0.23.tgz", - "integrity": "sha512-pWReu3fkohwyvztx/oQWWgld2iad25TfUdi6wvhhaDPIQjHU/pyvlKgXFw1kX31SQK2Nq9MH+vRDWB0ZLy8fYw==", - "dev": true, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/globals": { - "version": "13.23.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", - "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/eslint/node_modules/optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "dependencies": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/eslint/node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/eslint/node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/execa": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", - "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": "^14.18.0 || ^16.14.0 || >=18.0.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/execa/node_modules/human-signals": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", - "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", - "engines": { - "node": ">=14.18.0" - } - }, - "node_modules/execa/node_modules/is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/execa/node_modules/mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/execa/node_modules/npm-run-path": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", - "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/execa/node_modules/onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/execa/node_modules/path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/execa/node_modules/strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/extract-pr-titles": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/extract-pr-titles/-/extract-pr-titles-1.1.0.tgz", - "integrity": "sha512-B6jA1NqdIOuQGU2+19zIPpN9Ibg55KqAh+NChpl5Nn3A4+D7ohiDUZz14urr5bKzxXYtwnllxVfXg8tdvNHa3A==", - "dev": true, - "dependencies": { - "minimisted": "^2.0.0", - "pupa": "^2.0.1", - "simple-git": "^1.126.0" - }, - "bin": { - "extract-pr-titles": "bin/main.js" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "node_modules/fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==" - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" - }, - "node_modules/fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==" - }, - "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "node_modules/internal-slot": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", - "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.2", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "node_modules/is-async-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", - "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dev": true, - "dependencies": { - "hasown": "^2.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-docker": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-finalizationregistry": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", - "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-inside-container": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "dependencies": { - "is-docker": "^3.0.0" - }, - "bin": { - "is-inside-container": "cli.js" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-set": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", - "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", - "dev": true, - "dependencies": { - "which-typed-array": "^1.1.11" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakset": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", - "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-wsl/node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.1.tgz", - "integrity": "sha512-opCrKqbthmq3SKZ10mFMQG9dk3fTa3quaOLD35kJa5ejwZHd9xAr+kLuziiZz2cG32s4lMZxNdmdcEQnTDP4+g==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz", - "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==", - "dev": true, - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", - "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/iterator.prototype": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", - "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", - "dev": true, - "dependencies": { - "define-properties": "^1.2.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "reflect.getprototypeof": "^1.0.4", - "set-function-name": "^2.0.1" - } - }, - "node_modules/jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "dev": true, - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-changed-files/node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime/node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", - "dev": true, - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/language-subtag-registry": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", - "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==", - "dev": true - }, - "node_modules/language-tags": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", - "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", - "dev": true, - "dependencies": { - "language-subtag-registry": "^0.3.20" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", - "dev": true - }, - "node_modules/lodash.kebabcase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", - "integrity": "sha1-hImxyw0p/4gZXM7KRI/21swpXDY=", - "dev": true - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" - }, - "node_modules/lodash.snakecase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", - "integrity": "sha1-OdcUo1NXFHg3rv1ktdy7Fr7Nj40=", - "dev": true - }, - "node_modules/lodash.upperfirst": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", - "integrity": "sha1-E2Xt9DFIBIHvDRxolXpe2Z1J984=", - "dev": true - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minimisted": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/minimisted/-/minimisted-2.0.1.tgz", - "integrity": "sha512-1oPjfuLQa2caorJUM8HV8lGgWCc0qqAO1MNv/k05G4qslmsndV/5WdNZrqCiyqiz3wohia2Ij2B7w2Dr7/IyrA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node_modules/node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", - "dev": true - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", - "dev": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.entries": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", - "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", - "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.groupby": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz", - "integrity": "sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1" - } - }, - "node_modules/object.values": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", - "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/open": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", - "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", - "dependencies": { - "default-browser": "^4.0.0", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/papaparse": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz", - "integrity": "sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw==" - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, - "node_modules/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/prettier": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", - "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "dependencies": { - "fast-diff": "^1.1.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/pretty-format/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "engines": { - "node": ">=6" - } - }, - "node_modules/pupa": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", - "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", - "dev": true, - "dependencies": { - "escape-goat": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pure-rand": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", - "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ] - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/reflect.getprototypeof": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz", - "integrity": "sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "globalthis": "^1.0.3", - "which-builtin-type": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==", - "dev": true - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", - "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "set-function-name": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/rereadable-stream": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/rereadable-stream/-/rereadable-stream-1.4.14.tgz", - "integrity": "sha512-vGANaSU3Uvl33Lz88otjkoXkiBx01KjrusdsW2K95JbJveWZdjf11CyutMRZyy7nj7NyCTRynauTbaiM7MCgkg==", - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/run-applescript": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", - "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", - "dependencies": { - "execa": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/run-applescript/node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-array-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", - "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/scramjet": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/scramjet/-/scramjet-4.37.0.tgz", - "integrity": "sha512-Y6b59qGsulkr5MxiVn9CABnL9pE/sPKihCcWSUhzZc6W0YWbfLWRXc1fE1M40QKfOQUBxks81efzJ7WpEuFmlQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/scramjetorg" - }, - { - "type": "individual", - "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=7F7V65C43EBMW" - } - ], - "dependencies": { - "papaparse": "^5.4.1", - "rereadable-stream": "^1.4.14", - "scramjet-core": "^4.32.12" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/scramjet-core": { - "version": "4.32.12", - "resolved": "https://registry.npmjs.org/scramjet-core/-/scramjet-core-4.32.12.tgz", - "integrity": "sha512-FkNaZqzXvzqdwrUWzMztJq2RUBcpBlm08zOYIhA69+//FzgrespLBz7DmCXdXfujjvmUIFGgq/T3aPFy1ctonw==", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", - "dev": true, - "dependencies": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/set-function-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", - "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", - "dev": true, - "dependencies": { - "define-data-property": "^1.0.1", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "node_modules/simple-git": { - "version": "1.132.0", - "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-1.132.0.tgz", - "integrity": "sha512-xauHm1YqCTom1sC9eOjfq3/9RKiUA9iPnxBbrY2DdL8l4ADMu0jjM5l5lphQP5YWNqAL2aXC/OeuQ76vHtW5fg==", - "dev": true, - "dependencies": { - "debug": "^4.0.1" - } - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/string.prototype.trim": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", - "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", - "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", - "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/svg-element-attributes": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/svg-element-attributes/-/svg-element-attributes-1.3.1.tgz", - "integrity": "sha512-Bh05dSOnJBf3miNMqpsormfNtfidA/GxQVakhtn0T4DECWKeXQRQUceYjJ+OxYiiLdGe4Jo9iFV8wICFapFeIA==", - "dev": true - }, - "node_modules/synckit": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", - "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", - "dependencies": { - "@pkgr/utils": "^2.3.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" - }, - "node_modules/titleize": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", - "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/ts-api-utils": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", - "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", - "dev": true, - "engines": { - "node": ">=16.13.0" - }, - "peerDependencies": { - "typescript": ">=4.2.0" - } - }, - "node_modules/ts-jest": { - "version": "29.1.1", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz", - "integrity": "sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==", - "dev": true, - "dependencies": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^29.0.0", - "json5": "^2.2.3", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "^7.5.3", - "yargs-parser": "^21.0.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/types": "^29.0.0", - "babel-jest": "^29.0.0", - "jest": "^29.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - } - } - }, - "node_modules/ts-jest/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tsconfig-paths": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", - "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", - "dev": true, - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tsconfig-paths/node_modules/json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/tunnel": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", - "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", - "engines": { - "node": ">=0.6.11 <=0.7.0 || >=0.7.3" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typed-array-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", - "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/typed-array-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", - "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", - "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/undici": { - "version": "5.27.2", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.27.2.tgz", - "integrity": "sha512-iS857PdOEy/y3wlM3yRp+6SNQQ6xU0mmZcwRSriqk+et/cwWAtwmIGf6WkoDN2EK/AMdCO/dfXzIwi+rFMrjjQ==", - "dependencies": { - "@fastify/busboy": "^2.0.0" - }, - "engines": { - "node": ">=14.0" - } - }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true - }, - "node_modules/universal-user-agent": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", - "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==" - }, - "node_modules/untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", - "engines": { - "node": ">=8" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/v8-to-istanbul": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.3.tgz", - "integrity": "sha512-9lDD+EVI2fjFsMWXc6dy5JJzBsVTcQ2fVkfBvncZ6xJWG9wtBhOldG+mHkSL0+V1K/xgZz0JDO5UT5hFwHUghg==", - "dev": true, - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-builtin-type": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", - "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", - "dev": true, - "dependencies": { - "function.prototype.name": "^1.1.5", - "has-tostringtag": "^1.0.0", - "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", - "is-finalizationregistry": "^1.0.2", - "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-collection": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", - "dev": true, - "dependencies": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", - "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", - "dev": true, - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.4", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - } - }, - "dependencies": { - "@aashutoshrathi/word-wrap": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", - "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==" - }, - "@actions/core": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.10.1.tgz", - "integrity": "sha512-3lBR9EDAY+iYIpTnTIXmWcNbX3T2kCkAEQGIQx4NVQ0575nk2k3GRZDTPQG+vVtS2izSLmINlxXf0uLtnrTP+g==", - "requires": { - "@actions/http-client": "^2.0.1", - "uuid": "^8.3.2" - } - }, - "@actions/github": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@actions/github/-/github-6.0.0.tgz", - "integrity": "sha512-alScpSVnYmjNEXboZjarjukQEzgCRmjMv6Xj47fsdnqGS73bjJNDpiiXmp8jr0UZLdUB6d9jW63IcmddUP+l0g==", - "requires": { - "@actions/http-client": "^2.2.0", - "@octokit/core": "^5.0.1", - "@octokit/plugin-paginate-rest": "^9.0.0", - "@octokit/plugin-rest-endpoint-methods": "^10.0.0" - } - }, - "@actions/http-client": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-2.2.0.tgz", - "integrity": "sha512-q+epW0trjVUUHboliPb4UF9g2msf+w61b32tAkFEwL/IwP0DQWgbCMM0Hbe3e3WXSKz5VcUXbzJQgy8Hkra/Lg==", - "requires": { - "tunnel": "^0.0.6", - "undici": "^5.25.4" - } - }, - "@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "dev": true, - "requires": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", - "dev": true, - "requires": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/compat-data": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.2.tgz", - "integrity": "sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==", - "dev": true - }, - "@babel/core": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.23.2.tgz", - "integrity": "sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==", - "dev": true, - "requires": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-compilation-targets": "^7.22.15", - "@babel/helper-module-transforms": "^7.23.0", - "@babel/helpers": "^7.23.2", - "@babel/parser": "^7.23.0", - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - } - }, - "@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", - "dev": true, - "requires": { - "@babel/types": "^7.23.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - } - }, - "@babel/helper-compilation-targets": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz", - "integrity": "sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==", - "dev": true, - "requires": { - "@babel/compat-data": "^7.22.9", - "@babel/helper-validator-option": "^7.22.15", - "browserslist": "^4.21.9", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "dependencies": { - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - } - } - }, - "@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true - }, - "@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dev": true, - "requires": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - } - }, - "@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-module-imports": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", - "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", - "dev": true, - "requires": { - "@babel/types": "^7.22.15" - } - }, - "@babel/helper-module-transforms": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.0.tgz", - "integrity": "sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==", - "dev": true, - "requires": { - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-module-imports": "^7.22.15", - "@babel/helper-simple-access": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/helper-validator-identifier": "^7.22.20" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", - "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", - "dev": true - }, - "@babel/helper-simple-access": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", - "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true - }, - "@babel/helper-validator-option": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.15.tgz", - "integrity": "sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==", - "dev": true - }, - "@babel/helpers": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.2.tgz", - "integrity": "sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==", - "dev": true, - "requires": { - "@babel/template": "^7.22.15", - "@babel/traverse": "^7.23.2", - "@babel/types": "^7.23.0" - } - }, - "@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", - "dev": true - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.12.13" - } - }, - "@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.14.5" - } - }, - "@babel/plugin-syntax-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.22.5" - } - }, - "@babel/runtime": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.2.tgz", - "integrity": "sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==", - "dev": true, - "requires": { - "regenerator-runtime": "^0.14.0" - } - }, - "@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - } - }, - "@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - } - }, - "@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - } - }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", - "requires": { - "eslint-visitor-keys": "^3.3.0" - } - }, - "@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==" - }, - "@eslint/eslintrc": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz", - "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==", - "requires": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "globals": { - "version": "13.23.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", - "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", - "requires": { - "type-fest": "^0.20.2" - } - } - } - }, - "@eslint/js": { - "version": "8.53.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz", - "integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==" - }, - "@fastify/busboy": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.0.0.tgz", - "integrity": "sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ==" - }, - "@github/browserslist-config": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@github/browserslist-config/-/browserslist-config-1.0.0.tgz", - "integrity": "sha512-gIhjdJp/c2beaIWWIlsXdqXVRUz3r2BxBCpfz/F3JXHvSAQ1paMYjLH+maEATtENg+k5eLV7gA+9yPp762ieuw==", - "dev": true - }, - "@humanwhocodes/config-array": { - "version": "0.11.13", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", - "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", - "requires": { - "@humanwhocodes/object-schema": "^2.0.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - } - }, - "@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==" - }, - "@humanwhocodes/object-schema": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", - "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==" - }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "dependencies": { - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } - } - }, - "@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true - }, - "@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - } - }, - "@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "requires": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "requires": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - } - }, - "@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "requires": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - } - }, - "@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "requires": { - "jest-get-type": "^29.6.3" - } - }, - "@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - } - }, - "@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "requires": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - } - }, - "@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - } - }, - "@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "requires": { - "@sinclair/typebox": "^0.27.8" - } - }, - "@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - } - }, - "@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "requires": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "requires": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - } - }, - "@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - } - }, - "@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "requires": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - } - }, - "@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", - "dev": true, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" - } - }, - "@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true - }, - "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.20", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", - "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==" - }, - "@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@octokit/auth-token": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", - "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==" - }, - "@octokit/core": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.0.1.tgz", - "integrity": "sha512-lyeeeZyESFo+ffI801SaBKmCfsvarO+dgV8/0gD8u1d87clbEdWsP5yC+dSj3zLhb2eIf5SJrn6vDz9AheETHw==", - "requires": { - "@octokit/auth-token": "^4.0.0", - "@octokit/graphql": "^7.0.0", - "@octokit/request": "^8.0.2", - "@octokit/request-error": "^5.0.0", - "@octokit/types": "^12.0.0", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/endpoint": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.2.tgz", - "integrity": "sha512-qhKW8YLIi+Kmc92FQUFGr++DYtkx/1fBv+Thua6baqnjnOsgBYJDCvWZR1YcINuHGOEQt416WOfE+A/oG60NBQ==", - "requires": { - "@octokit/types": "^12.0.0", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/graphql": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.2.tgz", - "integrity": "sha512-OJ2iGMtj5Tg3s6RaXH22cJcxXRi7Y3EBqbHTBRq+PQAqfaS8f/236fUrWhfSn8P4jovyzqucxme7/vWSSZBX2Q==", - "requires": { - "@octokit/request": "^8.0.1", - "@octokit/types": "^12.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/openapi-types": { - "version": "19.0.2", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-19.0.2.tgz", - "integrity": "sha512-8li32fUDUeml/ACRp/njCWTsk5t17cfTM1jp9n08pBrqs5cDFJubtjsSnuz56r5Tad6jdEPJld7LxNp9dNcyjQ==" - }, - "@octokit/plugin-paginate-rest": { - "version": "9.1.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.1.2.tgz", - "integrity": "sha512-euDbNV6fxX6btsCDnZoZM4vw3zO1nj1Z7TskHAulO6mZ9lHoFTpwll6farf+wh31mlBabgU81bBYdflp0GLVAQ==", - "requires": { - "@octokit/types": "^12.1.1" - } - }, - "@octokit/plugin-rest-endpoint-methods": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.1.2.tgz", - "integrity": "sha512-JztgZ82CY4JNlPTuF0jh4iWuuGpEi5czFCoXyAbMg4F2XyFBbG5DWAKfa3odRvdZww6Df1tQgBKnqpd9X0WF9g==", - "requires": { - "@octokit/types": "^12.1.1" - } - }, - "@octokit/request": { - "version": "8.1.4", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.1.4.tgz", - "integrity": "sha512-M0aaFfpGPEKrg7XoA/gwgRvc9MSXHRO2Ioki1qrPDbl1e9YhjIwVoHE7HIKmv/m3idzldj//xBujcFNqGX6ENA==", - "requires": { - "@octokit/endpoint": "^9.0.0", - "@octokit/request-error": "^5.0.0", - "@octokit/types": "^12.0.0", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" - } - }, - "@octokit/request-error": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.0.1.tgz", - "integrity": "sha512-X7pnyTMV7MgtGmiXBwmO6M5kIPrntOXdyKZLigNfQWSEQzVxR4a4vo49vJjTWX70mPndj8KhfT4Dx+2Ng3vnBQ==", - "requires": { - "@octokit/types": "^12.0.0", - "deprecation": "^2.0.0", - "once": "^1.4.0" - } - }, - "@octokit/types": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.1.1.tgz", - "integrity": "sha512-qnJTldJ1NyGT5MTsCg/Zi+y2IFHZ1Jo5+njNCjJ9FcainV7LjuHgmB697kA0g4MjZeDAJsM3B45iqCVsCLVFZg==", - "requires": { - "@octokit/openapi-types": "^19.0.2" - } - }, - "@octokit/webhooks-types": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/@octokit/webhooks-types/-/webhooks-types-7.3.1.tgz", - "integrity": "sha512-u6355ZsZnHwmxen30SrqnYb1pXieBFkYgkNzt+Ed4Ao5tupN1OErHfzwiV6hq6duGkDAYASbq7/uVJQ69PjLEg==", - "dev": true - }, - "@pkgr/utils": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz", - "integrity": "sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw==", - "requires": { - "cross-spawn": "^7.0.3", - "fast-glob": "^3.3.0", - "is-glob": "^4.0.3", - "open": "^9.1.0", - "picocolors": "^1.0.0", - "tslib": "^2.6.0" - } - }, - "@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true - }, - "@sinonjs/commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", - "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "requires": { - "@sinonjs/commons": "^3.0.0" - } - }, - "@types/babel__core": { - "version": "7.20.4", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.4.tgz", - "integrity": "sha512-mLnSC22IC4vcWiuObSRjrLd9XcBTGf59vUSoq2jkQDJ/QQ8PMI9rSuzE+aEV8karUMbskw07bKYoUJCKTUaygg==", - "dev": true, - "requires": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@types/babel__generator": { - "version": "7.6.7", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.7.tgz", - "integrity": "sha512-6Sfsq+EaaLrw4RmdFWE9Onp63TOUue71AWb4Gpa6JxzgTYtimbM086WnYTy2U67AofR++QKCo08ZP6pwx8YFHQ==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.20.4", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.4.tgz", - "integrity": "sha512-mSM/iKUk5fDDrEV/e83qY+Cr3I1+Q3qqTuEn++HAWYjEa1+NxZr6CNrcJGf2ZTnq4HoFGC3zaTPZTobCzCFukA==", - "dev": true, - "requires": { - "@babel/types": "^7.20.7" - } - }, - "@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz", - "integrity": "sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg==", - "dev": true - }, - "@types/istanbul-lib-report": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz", - "integrity": "sha512-3BUTyMzbZa2DtDI2BkERNC6jJw2Mr2Y0oGI7mRxYNBPxppbtEK1F66u3bKwU2g+wxwWI7PAoRpJnOY1grJqzHg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/jest": { - "version": "29.5.8", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.8.tgz", - "integrity": "sha512-fXEFTxMV2Co8ZF5aYFJv+YeA08RTYJfhtN5c9JSv/mFEMe+xxjufCb+PHL+bJcMs/ebPUsBu+UNTEz+ydXrR6g==", - "dev": true, - "requires": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true - }, - "@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "@types/node": { - "version": "20.9.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.9.0.tgz", - "integrity": "sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==", - "dev": true, - "requires": { - "undici-types": "~5.26.4" - } - }, - "@types/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==", - "dev": true - }, - "@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true - }, - "@types/yargs": { - "version": "17.0.30", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.30.tgz", - "integrity": "sha512-3SJLzYk3yz3EgI9I8OLoH06B3PdXIoU2imrBZzaGqUtUXf5iUNDtmAfCGuQrny1bnmyjh/GM/YNts6WK5jR5Rw==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-13.1.0.tgz", - "integrity": "sha512-gCubfBUZ6KxzoibJ+SCUc/57Ms1jz5NjHe4+dI2krNmU5zCPAphyLJYyTOg06ueIyfj+SaCUqmzun7ImlxDcKg==", - "dev": true - }, - "@typescript-eslint/eslint-plugin": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.10.0.tgz", - "integrity": "sha512-uoLj4g2OTL8rfUQVx2AFO1hp/zja1wABJq77P6IclQs6I/m9GLrm7jCdgzZkvWdDCQf1uEvoa8s8CupsgWQgVg==", - "dev": true, - "requires": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.10.0", - "@typescript-eslint/type-utils": "6.10.0", - "@typescript-eslint/utils": "6.10.0", - "@typescript-eslint/visitor-keys": "6.10.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.4", - "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "@typescript-eslint/parser": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.10.0.tgz", - "integrity": "sha512-+sZwIj+s+io9ozSxIWbNB5873OSdfeBEH/FR0re14WLI6BaKuSOnnwCJ2foUiu8uXf4dRp1UqHP0vrZ1zXGrog==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "6.10.0", - "@typescript-eslint/types": "6.10.0", - "@typescript-eslint/typescript-estree": "6.10.0", - "@typescript-eslint/visitor-keys": "6.10.0", - "debug": "^4.3.4" - } - }, - "@typescript-eslint/scope-manager": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.10.0.tgz", - "integrity": "sha512-TN/plV7dzqqC2iPNf1KrxozDgZs53Gfgg5ZHyw8erd6jd5Ta/JIEcdCheXFt9b1NYb93a1wmIIVW/2gLkombDg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.10.0", - "@typescript-eslint/visitor-keys": "6.10.0" - } - }, - "@typescript-eslint/type-utils": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.10.0.tgz", - "integrity": "sha512-wYpPs3hgTFblMYwbYWPT3eZtaDOjbLyIYuqpwuLBBqhLiuvJ+9sEp2gNRJEtR5N/c9G1uTtQQL5AhV0fEPJYcg==", - "dev": true, - "requires": { - "@typescript-eslint/typescript-estree": "6.10.0", - "@typescript-eslint/utils": "6.10.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" - } - }, - "@typescript-eslint/types": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.10.0.tgz", - "integrity": "sha512-36Fq1PWh9dusgo3vH7qmQAj5/AZqARky1Wi6WpINxB6SkQdY5vQoT2/7rW7uBIsPDcvvGCLi4r10p0OJ7ITAeg==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.10.0.tgz", - "integrity": "sha512-ek0Eyuy6P15LJVeghbWhSrBCj/vJpPXXR+EpaRZqou7achUWL8IdYnMSC5WHAeTWswYQuP2hAZgij/bC9fanBg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.10.0", - "@typescript-eslint/visitor-keys": "6.10.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "@typescript-eslint/utils": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.10.0.tgz", - "integrity": "sha512-v+pJ1/RcVyRc0o4wAGux9x42RHmAjIGzPRo538Z8M1tVx6HOnoQBCX/NoadHQlZeC+QO2yr4nNSFWOoraZCAyg==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.10.0", - "@typescript-eslint/types": "6.10.0", - "@typescript-eslint/typescript-estree": "6.10.0", - "semver": "^7.5.4" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "@typescript-eslint/visitor-keys": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.10.0.tgz", - "integrity": "sha512-xMGluxQIEtOM7bqFCo+rCMh5fqI+ZxV5RUUOa29iVPz1OgCZrtc7rFnz5cLUazlkPKYqX+75iuDq7m0HQ48nCg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "6.10.0", - "eslint-visitor-keys": "^3.4.1" - } - }, - "@ungap/structured-clone": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", - "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" - }, - "@vercel/ncc": { - "version": "0.38.1", - "resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.38.1.tgz", - "integrity": "sha512-IBBb+iI2NLu4VQn3Vwldyi2QwaXt5+hTyh58ggAMoCGE6DJmPvwL3KPBWcJl1m9LYPChBLE980Jw+CS4Wokqxw==", - "dev": true - }, - "acorn": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", - "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==" - }, - "acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "requires": {} - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "requires": { - "type-fest": "^0.21.3" - }, - "dependencies": { - "type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true - } - } - }, - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" - }, - "aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "dev": true, - "requires": { - "dequal": "^2.0.3" - } - }, - "array-buffer-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", - "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" - } - }, - "array-includes": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz", - "integrity": "sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-string": "^1.0.7" - } - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "array.prototype.findlastindex": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz", - "integrity": "sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.2.1" - } - }, - "array.prototype.flat": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz", - "integrity": "sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - } - }, - "array.prototype.flatmap": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz", - "integrity": "sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "es-shim-unscopables": "^1.0.0" - } - }, - "arraybuffer.prototype.slice": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", - "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", - "dev": true, - "requires": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "is-array-buffer": "^3.0.2", - "is-shared-array-buffer": "^1.0.2" - } - }, - "ast-types-flow": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", - "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", - "dev": true - }, - "asynciterator.prototype": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/asynciterator.prototype/-/asynciterator.prototype-1.0.0.tgz", - "integrity": "sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==", - "dev": true, - "requires": { - "has-symbols": "^1.0.3" - } - }, - "available-typed-arrays": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", - "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", - "dev": true - }, - "axe-core": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.7.0.tgz", - "integrity": "sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==", - "dev": true - }, - "axobject-query": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", - "integrity": "sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==", - "dev": true, - "requires": { - "dequal": "^2.0.3" - } - }, - "babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "requires": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - } - }, - "babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "dependencies": { - "istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - } - } - } - }, - "babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", - "dev": true, - "requires": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" - } - }, - "babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "before-after-hook": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", - "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" - }, - "big-integer": { - "version": "1.6.51", - "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", - "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==" - }, - "bplist-parser": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz", - "integrity": "sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw==", - "requires": { - "big-integer": "^1.6.44" - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "requires": { - "fill-range": "^7.0.1" - } - }, - "browserslist": { - "version": "4.22.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.22.1.tgz", - "integrity": "sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==", - "dev": true, - "requires": { - "caniuse-lite": "^1.0.30001541", - "electron-to-chromium": "^1.4.535", - "node-releases": "^2.0.13", - "update-browserslist-db": "^1.0.13" - } - }, - "bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "requires": { - "fast-json-stable-stringify": "2.x" - } - }, - "bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, - "buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true - }, - "bundle-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz", - "integrity": "sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw==", - "requires": { - "run-applescript": "^5.0.0" - } - }, - "call-bind": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", - "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", - "dev": true, - "requires": { - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.1", - "set-function-length": "^1.1.1" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "caniuse-lite": { - "version": "1.0.30001561", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001561.tgz", - "integrity": "sha512-NTt0DNoKe958Q0BE0j0c1V9jbUzhBxHIEJy7asmGrpE0yG63KTV7PLHPnK2E1O9RsQrQ081I3NLuXGS6zht3cw==", - "dev": true - }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true - }, - "ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true - }, - "cjs-module-lexer": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", - "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", - "dev": true - }, - "cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - } - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true - }, - "collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - } - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "damerau-levenshtein": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", - "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", - "dev": true - }, - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - }, - "dedent": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", - "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", - "dev": true, - "requires": {} - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" - }, - "deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true - }, - "default-browser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz", - "integrity": "sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA==", - "requires": { - "bundle-name": "^3.0.0", - "default-browser-id": "^3.0.0", - "execa": "^7.1.1", - "titleize": "^3.0.0" - } - }, - "default-browser-id": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz", - "integrity": "sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA==", - "requires": { - "bplist-parser": "^0.2.0", - "untildify": "^4.0.0" - } - }, - "define-data-property": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", - "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", - "dev": true, - "requires": { - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" - } - }, - "define-lazy-prop": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", - "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==" - }, - "define-properties": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", - "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, - "requires": { - "define-data-property": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - } - }, - "deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" - }, - "dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true - }, - "detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true - }, - "diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "requires": { - "esutils": "^2.0.2" - } - }, - "electron-to-chromium": { - "version": "1.4.577", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.577.tgz", - "integrity": "sha512-/5xHPH6f00SxhHw6052r+5S1xO7gHNc89hV7tqlvnStvKbSrDqc/u6AlwPvVWWNj+s4/KL6T6y8ih+nOY0qYNA==", - "dev": true - }, - "emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true - }, - "emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-abstract": { - "version": "1.22.3", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", - "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", - "dev": true, - "requires": { - "array-buffer-byte-length": "^1.0.0", - "arraybuffer.prototype.slice": "^1.0.2", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.5", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.6", - "get-intrinsic": "^1.2.2", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.12", - "is-weakref": "^1.0.2", - "object-inspect": "^1.13.1", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "safe-array-concat": "^1.0.1", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.8", - "string.prototype.trimend": "^1.0.7", - "string.prototype.trimstart": "^1.0.7", - "typed-array-buffer": "^1.0.0", - "typed-array-byte-length": "^1.0.0", - "typed-array-byte-offset": "^1.0.0", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.13" - } - }, - "es-iterator-helpers": { - "version": "1.0.15", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.0.15.tgz", - "integrity": "sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==", - "dev": true, - "requires": { - "asynciterator.prototype": "^1.0.0", - "call-bind": "^1.0.2", - "define-properties": "^1.2.1", - "es-abstract": "^1.22.1", - "es-set-tostringtag": "^2.0.1", - "function-bind": "^1.1.1", - "get-intrinsic": "^1.2.1", - "globalthis": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "iterator.prototype": "^1.1.2", - "safe-array-concat": "^1.0.1" - } - }, - "es-set-tostringtag": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", - "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", - "dev": true, - "requires": { - "get-intrinsic": "^1.2.2", - "has-tostringtag": "^1.0.0", - "hasown": "^2.0.0" - } - }, - "es-shim-unscopables": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", - "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", - "dev": true, - "requires": { - "hasown": "^2.0.0" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true - }, - "escape-goat": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", - "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "eslint": { - "version": "8.53.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz", - "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==", - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.3", - "@eslint/js": "8.53.0", - "@humanwhocodes/config-array": "^0.11.13", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" - }, - "globals": { - "version": "13.23.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", - "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", - "requires": { - "type-fest": "^0.20.2" - } - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "optionator": { - "version": "0.9.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", - "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", - "requires": { - "@aashutoshrathi/word-wrap": "^1.2.3", - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0" - } - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "requires": { - "prelude-ls": "^1.2.1" - } - } - } - }, - "eslint-config-prettier": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz", - "integrity": "sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew==", - "dev": true - }, - "eslint-import-resolver-node": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", - "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", - "dev": true, - "requires": { - "debug": "^3.2.7", - "is-core-module": "^2.13.0", - "resolve": "^1.22.4" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "eslint-module-utils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz", - "integrity": "sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==", - "dev": true, - "requires": { - "debug": "^3.2.7" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } - } - }, - "eslint-plugin-escompat": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-escompat/-/eslint-plugin-escompat-3.4.0.tgz", - "integrity": "sha512-ufTPv8cwCxTNoLnTZBFTQ5SxU2w7E7wiMIS7PSxsgP1eAxFjtSaoZ80LRn64hI8iYziE6kJG6gX/ZCJVxh48Bg==", - "dev": true, - "requires": { - "browserslist": "^4.21.0" - } - }, - "eslint-plugin-eslint-comments": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-3.2.0.tgz", - "integrity": "sha512-0jkOl0hfojIHHmEHgmNdqv4fmh7300NdpA9FFpF7zaoLvB/QeXOGNLIo86oAveJFrfB1p05kC8hpEMHM8DwWVQ==", - "dev": true, - "requires": { - "escape-string-regexp": "^1.0.5", - "ignore": "^5.0.5" - } - }, - "eslint-plugin-filenames": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-filenames/-/eslint-plugin-filenames-1.3.2.tgz", - "integrity": "sha512-tqxJTiEM5a0JmRCUYQmxw23vtTxrb2+a3Q2mMOPhFxvt7ZQQJmdiuMby9B/vUAuVMghyP7oET+nIf6EO6CBd/w==", - "dev": true, - "requires": { - "lodash.camelcase": "4.3.0", - "lodash.kebabcase": "4.1.1", - "lodash.snakecase": "4.1.1", - "lodash.upperfirst": "4.3.1" - } - }, - "eslint-plugin-github": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-github/-/eslint-plugin-github-4.10.1.tgz", - "integrity": "sha512-1AqQBockOM+m0ZUpwfjWtX0lWdX5cRi/hwJnSNvXoOmz/Hh+ULH6QFz6ENWueTWjoWpgPv0af3bj+snps6o4og==", - "dev": true, - "requires": { - "@github/browserslist-config": "^1.0.0", - "@typescript-eslint/eslint-plugin": "^6.0.0", - "@typescript-eslint/parser": "^6.0.0", - "aria-query": "^5.3.0", - "eslint-config-prettier": ">=8.0.0", - "eslint-plugin-escompat": "^3.3.3", - "eslint-plugin-eslint-comments": "^3.2.0", - "eslint-plugin-filenames": "^1.3.2", - "eslint-plugin-i18n-text": "^1.0.1", - "eslint-plugin-import": "^2.25.2", - "eslint-plugin-jsx-a11y": "^6.7.1", - "eslint-plugin-no-only-tests": "^3.0.0", - "eslint-plugin-prettier": "^5.0.0", - "eslint-rule-documentation": ">=1.0.0", - "jsx-ast-utils": "^3.3.2", - "prettier": "^3.0.0", - "svg-element-attributes": "^1.3.1" - } - }, - "eslint-plugin-i18n-text": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-i18n-text/-/eslint-plugin-i18n-text-1.0.1.tgz", - "integrity": "sha512-3G3UetST6rdqhqW9SfcfzNYMpQXS7wNkJvp6dsXnjzGiku6Iu5hl3B0kmk6lIcFPwYjhQIY+tXVRtK9TlGT7RA==", - "dev": true, - "requires": {} - }, - "eslint-plugin-import": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.29.0.tgz", - "integrity": "sha512-QPOO5NO6Odv5lpoTkddtutccQjysJuFxoPS7fAHO+9m9udNHvTCPSAMW9zGAYj8lAIdr40I8yPCdUYrncXtrwg==", - "dev": true, - "requires": { - "array-includes": "^3.1.7", - "array.prototype.findlastindex": "^1.2.3", - "array.prototype.flat": "^1.3.2", - "array.prototype.flatmap": "^1.3.2", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.9", - "eslint-module-utils": "^2.8.0", - "hasown": "^2.0.0", - "is-core-module": "^2.13.1", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.fromentries": "^2.0.7", - "object.groupby": "^1.0.1", - "object.values": "^1.1.7", - "semver": "^6.3.1", - "tsconfig-paths": "^3.14.2" - }, - "dependencies": { - "debug": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", - "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - } - } - }, - "eslint-plugin-jest": { - "version": "27.6.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-27.6.0.tgz", - "integrity": "sha512-MTlusnnDMChbElsszJvrwD1dN3x6nZl//s4JD23BxB6MgR66TZlL064su24xEIS3VACfAoHV1vgyMgPw8nkdng==", - "dev": true, - "requires": { - "@typescript-eslint/utils": "^5.10.0" - }, - "dependencies": { - "@typescript-eslint/scope-manager": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", - "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0" - } - }, - "@typescript-eslint/types": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", - "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", - "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/visitor-keys": "5.62.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - } - }, - "@typescript-eslint/utils": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", - "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", - "dev": true, - "requires": { - "@eslint-community/eslint-utils": "^4.2.0", - "@types/json-schema": "^7.0.9", - "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.62.0", - "@typescript-eslint/types": "5.62.0", - "@typescript-eslint/typescript-estree": "5.62.0", - "eslint-scope": "^5.1.1", - "semver": "^7.3.7" - } - }, - "@typescript-eslint/visitor-keys": { - "version": "5.62.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", - "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "5.62.0", - "eslint-visitor-keys": "^3.3.0" - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "eslint-plugin-jsx-a11y": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.8.0.tgz", - "integrity": "sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==", - "dev": true, - "requires": { - "@babel/runtime": "^7.23.2", - "aria-query": "^5.3.0", - "array-includes": "^3.1.7", - "array.prototype.flatmap": "^1.3.2", - "ast-types-flow": "^0.0.8", - "axe-core": "=4.7.0", - "axobject-query": "^3.2.1", - "damerau-levenshtein": "^1.0.8", - "emoji-regex": "^9.2.2", - "es-iterator-helpers": "^1.0.15", - "hasown": "^2.0.0", - "jsx-ast-utils": "^3.3.5", - "language-tags": "^1.0.9", - "minimatch": "^3.1.2", - "object.entries": "^1.1.7", - "object.fromentries": "^2.0.7" - } - }, - "eslint-plugin-no-only-tests": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-no-only-tests/-/eslint-plugin-no-only-tests-3.1.0.tgz", - "integrity": "sha512-Lf4YW/bL6Un1R6A76pRZyE1dl1vr31G/ev8UzIc/geCgFWyrKil8hVjYqWVKGB/UIGmb6Slzs9T0wNezdSVegw==", - "dev": true - }, - "eslint-plugin-prettier": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.1.tgz", - "integrity": "sha512-m3u5RnR56asrwV/lDC4GHorlW75DsFfmUcjfCYylTUs85dBRnB7VM6xG8eCMJdeDRnppzmxZVf1GEPJvl1JmNg==", - "requires": { - "prettier-linter-helpers": "^1.0.0", - "synckit": "^0.8.5" - } - }, - "eslint-rule-documentation": { - "version": "1.0.23", - "resolved": "https://registry.npmjs.org/eslint-rule-documentation/-/eslint-rule-documentation-1.0.23.tgz", - "integrity": "sha512-pWReu3fkohwyvztx/oQWWgld2iad25TfUdi6wvhhaDPIQjHU/pyvlKgXFw1kX31SQK2Nq9MH+vRDWB0ZLy8fYw==", - "dev": true - }, - "eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, - "eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==" - }, - "espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "requires": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", - "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", - "requires": { - "estraverse": "^5.1.0" - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "requires": { - "estraverse": "^5.2.0" - } - }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==" - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" - }, - "execa": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz", - "integrity": "sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==", - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" - }, - "dependencies": { - "human-signals": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", - "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==" - }, - "is-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", - "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==" - }, - "mimic-fn": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", - "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==" - }, - "npm-run-path": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", - "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", - "requires": { - "path-key": "^4.0.0" - } - }, - "onetime": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", - "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", - "requires": { - "mimic-fn": "^4.0.0" - } - }, - "path-key": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", - "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==" - }, - "strip-final-newline": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", - "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==" - } - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true - }, - "expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "requires": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - } - }, - "extract-pr-titles": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/extract-pr-titles/-/extract-pr-titles-1.1.0.tgz", - "integrity": "sha512-B6jA1NqdIOuQGU2+19zIPpN9Ibg55KqAh+NChpl5Nn3A4+D7ohiDUZz14urr5bKzxXYtwnllxVfXg8tdvNHa3A==", - "dev": true, - "requires": { - "minimisted": "^2.0.0", - "pupa": "^2.0.1", - "simple-git": "^1.126.0" - } - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "fast-diff": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", - "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==" - }, - "fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "requires": { - "is-glob": "^4.0.1" - } - } - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" - }, - "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", - "requires": { - "reusify": "^1.0.4" - } - }, - "fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "requires": { - "bser": "2.1.1" - } - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "requires": { - "flat-cache": "^3.0.4" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==" - }, - "for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", - "dev": true, - "requires": { - "is-callable": "^1.1.3" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" - }, - "fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true - }, - "function.prototype.name": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", - "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "functions-have-names": "^1.2.3" - } - }, - "functions-have-names": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", - "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true - }, - "gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-intrinsic": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", - "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", - "dev": true, - "requires": { - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - } - }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true - }, - "get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==" - }, - "get-symbol-description": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", - "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "requires": { - "is-glob": "^4.0.3" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "globalthis": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", - "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3" - } - }, - "globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - } - }, - "gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "requires": { - "get-intrinsic": "^1.1.3" - } - }, - "graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true - }, - "graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" - }, - "has-bigints": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", - "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" - }, - "has-property-descriptors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", - "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", - "dev": true, - "requires": { - "get-intrinsic": "^1.2.2" - } - }, - "has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true - }, - "has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true - }, - "has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "hasown": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", - "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", - "dev": true, - "requires": { - "function-bind": "^1.1.2" - } - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" - }, - "ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==" - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", - "dev": true, - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=" - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "internal-slot": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", - "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", - "dev": true, - "requires": { - "get-intrinsic": "^1.2.2", - "hasown": "^2.0.0", - "side-channel": "^1.0.4" - } - }, - "is-array-buffer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", - "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true - }, - "is-async-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.0.0.tgz", - "integrity": "sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-bigint": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", - "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", - "dev": true, - "requires": { - "has-bigints": "^1.0.1" - } - }, - "is-boolean-object": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", - "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true - }, - "is-core-module": { - "version": "2.13.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", - "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", - "dev": true, - "requires": { - "hasown": "^2.0.0" - } - }, - "is-date-object": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", - "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-docker": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==" - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" - }, - "is-finalizationregistry": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.0.2.tgz", - "integrity": "sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true - }, - "is-generator-function": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", - "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-inside-container": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "requires": { - "is-docker": "^3.0.0" - } - }, - "is-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.2.tgz", - "integrity": "sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==", - "dev": true - }, - "is-negative-zero": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", - "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" - }, - "is-number-object": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", - "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==" - }, - "is-plain-object": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", - "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==" - }, - "is-regex": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", - "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - } - }, - "is-set": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.2.tgz", - "integrity": "sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==", - "dev": true - }, - "is-shared-array-buffer": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", - "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==" - }, - "is-string": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", - "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", - "dev": true, - "requires": { - "has-tostringtag": "^1.0.0" - } - }, - "is-symbol": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", - "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", - "dev": true, - "requires": { - "has-symbols": "^1.0.2" - } - }, - "is-typed-array": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", - "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", - "dev": true, - "requires": { - "which-typed-array": "^1.1.11" - } - }, - "is-weakmap": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.1.tgz", - "integrity": "sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==", - "dev": true - }, - "is-weakref": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", - "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2" - } - }, - "is-weakset": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.2.tgz", - "integrity": "sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - } - }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "requires": { - "is-docker": "^2.0.0" - }, - "dependencies": { - "is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" - } - } - }, - "isarray": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", - "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, - "istanbul-lib-coverage": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.1.tgz", - "integrity": "sha512-opCrKqbthmq3SKZ10mFMQG9dk3fTa3quaOLD35kJa5ejwZHd9xAr+kLuziiZz2cG32s4lMZxNdmdcEQnTDP4+g==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz", - "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==", - "dev": true, - "requires": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - } - }, - "istanbul-reports": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", - "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "iterator.prototype": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", - "integrity": "sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==", - "dev": true, - "requires": { - "define-properties": "^1.2.1", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "reflect.getprototypeof": "^1.0.4", - "set-function-name": "^2.0.1" - } - }, - "jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "dev": true, - "requires": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - } - }, - "jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "requires": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "dependencies": { - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - } - } - }, - "jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "requires": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "requires": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - } - }, - "jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - } - }, - "jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - } - }, - "jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "requires": { - "detect-newline": "^3.0.0" - } - }, - "jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - } - }, - "jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "requires": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - } - }, - "jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true - }, - "jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.3.2", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - } - }, - "jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "requires": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - } - }, - "jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - } - }, - "jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - } - }, - "jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - } - }, - "jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "requires": {} - }, - "jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true - }, - "jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - } - }, - "jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "requires": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - } - }, - "jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "requires": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - } - }, - "jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "requires": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "dependencies": { - "strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - } - } - }, - "jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "requires": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - } - }, - "jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, - "requires": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "dependencies": { - "camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true - } - } - }, - "jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "requires": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - } - }, - "jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, - "requires": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "dependencies": { - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "requires": { - "argparse": "^2.0.1" - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=" - }, - "json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true - }, - "jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", - "dev": true, - "requires": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" - } - }, - "kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true - }, - "language-subtag-registry": { - "version": "0.3.22", - "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz", - "integrity": "sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==", - "dev": true - }, - "language-tags": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", - "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", - "dev": true, - "requires": { - "language-subtag-registry": "^0.3.20" - } - }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - }, - "lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "requires": { - "p-locate": "^5.0.0" - } - }, - "lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY=", - "dev": true - }, - "lodash.kebabcase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz", - "integrity": "sha1-hImxyw0p/4gZXM7KRI/21swpXDY=", - "dev": true - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" - }, - "lodash.snakecase": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz", - "integrity": "sha1-OdcUo1NXFHg3rv1ktdy7Fr7Nj40=", - "dev": true - }, - "lodash.upperfirst": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz", - "integrity": "sha1-E2Xt9DFIBIHvDRxolXpe2Z1J984=", - "dev": true - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "requires": { - "semver": "^7.5.3" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "requires": { - "tmpl": "1.0.5" - } - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==" - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==" - }, - "minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true - }, - "minimisted": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/minimisted/-/minimisted-2.0.1.tgz", - "integrity": "sha512-1oPjfuLQa2caorJUM8HV8lGgWCc0qqAO1MNv/k05G4qslmsndV/5WdNZrqCiyqiz3wohia2Ij2B7w2Dr7/IyrA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=" - }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true - }, - "node-releases": { - "version": "2.0.13", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", - "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "requires": { - "path-key": "^3.0.0" - } - }, - "object-inspect": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", - "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object.assign": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", - "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - } - }, - "object.entries": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz", - "integrity": "sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - } - }, - "object.fromentries": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz", - "integrity": "sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - } - }, - "object.groupby": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz", - "integrity": "sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1" - } - }, - "object.values": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", - "integrity": "sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "open": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/open/-/open-9.1.0.tgz", - "integrity": "sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg==", - "requires": { - "default-browser": "^4.0.0", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "is-wsl": "^2.2.0" - } - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "requires": { - "yocto-queue": "^0.1.0" - } - }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "requires": { - "p-limit": "^3.0.2" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "papaparse": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/papaparse/-/papaparse-5.4.1.tgz", - "integrity": "sha512-HipMsgJkZu8br23pW15uvo6sib6wne/4woLZPlFf3rpDyMe9ywEXUsuD7+6K9PRkJlVT51j/sCOYDKGGS3ZJrw==" - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==" - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" - }, - "path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true - }, - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" - }, - "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==" - }, - "pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "dev": true - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - }, - "dependencies": { - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - } - } - }, - "prettier": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", - "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==" - }, - "prettier-linter-helpers": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", - "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", - "requires": { - "fast-diff": "^1.1.2" - } - }, - "pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "requires": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true - }, - "react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", - "dev": true - } - } - }, - "prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "pupa": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", - "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", - "dev": true, - "requires": { - "escape-goat": "^2.0.0" - } - }, - "pure-rand": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", - "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==", - "dev": true - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" - }, - "reflect.getprototypeof": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.4.tgz", - "integrity": "sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1", - "get-intrinsic": "^1.2.1", - "globalthis": "^1.0.3", - "which-builtin-type": "^1.1.3" - } - }, - "regenerator-runtime": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==", - "dev": true - }, - "regexp.prototype.flags": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", - "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "set-function-name": "^2.0.0" - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true - }, - "rereadable-stream": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/rereadable-stream/-/rereadable-stream-1.4.14.tgz", - "integrity": "sha512-vGANaSU3Uvl33Lz88otjkoXkiBx01KjrusdsW2K95JbJveWZdjf11CyutMRZyy7nj7NyCTRynauTbaiM7MCgkg==" - }, - "resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "requires": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - } - }, - "resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - } - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==" - }, - "resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", - "dev": true - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==" - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "requires": { - "glob": "^7.1.3" - } - }, - "run-applescript": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz", - "integrity": "sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==", - "requires": { - "execa": "^5.0.0" - }, - "dependencies": { - "execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "requires": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - } - } - } - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "safe-array-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", - "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "has-symbols": "^1.0.3", - "isarray": "^2.0.5" - } - }, - "safe-regex-test": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", - "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" - } - }, - "scramjet": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/scramjet/-/scramjet-4.37.0.tgz", - "integrity": "sha512-Y6b59qGsulkr5MxiVn9CABnL9pE/sPKihCcWSUhzZc6W0YWbfLWRXc1fE1M40QKfOQUBxks81efzJ7WpEuFmlQ==", - "requires": { - "papaparse": "^5.4.1", - "rereadable-stream": "^1.4.14", - "scramjet-core": "^4.32.12" - } - }, - "scramjet-core": { - "version": "4.32.12", - "resolved": "https://registry.npmjs.org/scramjet-core/-/scramjet-core-4.32.12.tgz", - "integrity": "sha512-FkNaZqzXvzqdwrUWzMztJq2RUBcpBlm08zOYIhA69+//FzgrespLBz7DmCXdXfujjvmUIFGgq/T3aPFy1ctonw==" - }, - "semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true - }, - "set-function-length": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", - "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", - "dev": true, - "requires": { - "define-data-property": "^1.1.1", - "get-intrinsic": "^1.2.1", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.0" - } - }, - "set-function-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", - "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", - "dev": true, - "requires": { - "define-data-property": "^1.0.1", - "functions-have-names": "^1.2.3", - "has-property-descriptors": "^1.0.0" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" - }, - "side-channel": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", - "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - } - }, - "signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" - }, - "simple-git": { - "version": "1.132.0", - "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-1.132.0.tgz", - "integrity": "sha512-xauHm1YqCTom1sC9eOjfq3/9RKiUA9iPnxBbrY2DdL8l4ADMu0jjM5l5lphQP5YWNqAL2aXC/OeuQ76vHtW5fg==", - "dev": true, - "requires": { - "debug": "^4.0.1" - } - }, - "sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, - "stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - } - } - }, - "string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "requires": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - } - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "dependencies": { - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - } - } - }, - "string.prototype.trim": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", - "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - } - }, - "string.prototype.trimend": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", - "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - } - }, - "string.prototype.trimstart": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", - "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "es-abstract": "^1.22.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", - "dev": true - }, - "strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==" - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==" - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "requires": { - "has-flag": "^4.0.0" - } - }, - "supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true - }, - "svg-element-attributes": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/svg-element-attributes/-/svg-element-attributes-1.3.1.tgz", - "integrity": "sha512-Bh05dSOnJBf3miNMqpsormfNtfidA/GxQVakhtn0T4DECWKeXQRQUceYjJ+OxYiiLdGe4Jo9iFV8wICFapFeIA==", - "dev": true - }, - "synckit": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", - "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", - "requires": { - "@pkgr/utils": "^2.3.1", - "tslib": "^2.5.0" - } - }, - "test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "requires": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=" - }, - "titleize": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz", - "integrity": "sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ==" - }, - "tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "requires": { - "is-number": "^7.0.0" - } - }, - "ts-api-utils": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", - "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", - "dev": true, - "requires": {} - }, - "ts-jest": { - "version": "29.1.1", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz", - "integrity": "sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==", - "dev": true, - "requires": { - "bs-logger": "0.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "^29.0.0", - "json5": "^2.2.3", - "lodash.memoize": "4.x", - "make-error": "1.x", - "semver": "^7.5.3", - "yargs-parser": "^21.0.1" - }, - "dependencies": { - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "tsconfig-paths": { - "version": "3.14.2", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz", - "integrity": "sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==", - "dev": true, - "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "json5": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", - "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - } - } - }, - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - }, - "dependencies": { - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - } - } - }, - "tunnel": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", - "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==" - }, - "typed-array-buffer": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", - "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.1", - "is-typed-array": "^1.1.10" - } - }, - "typed-array-byte-length": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", - "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - } - }, - "typed-array-byte-offset": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", - "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", - "dev": true, - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "has-proto": "^1.0.1", - "is-typed-array": "^1.1.10" - } - }, - "typed-array-length": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", - "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" - } - }, - "typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", - "dev": true - }, - "unbox-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", - "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", - "dev": true, - "requires": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - } - }, - "undici": { - "version": "5.27.2", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.27.2.tgz", - "integrity": "sha512-iS857PdOEy/y3wlM3yRp+6SNQQ6xU0mmZcwRSriqk+et/cwWAtwmIGf6WkoDN2EK/AMdCO/dfXzIwi+rFMrjjQ==", - "requires": { - "@fastify/busboy": "^2.0.0" - } - }, - "undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true - }, - "universal-user-agent": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", - "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==" - }, - "untildify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", - "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==" - }, - "update-browserslist-db": { - "version": "1.0.13", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", - "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", - "dev": true, - "requires": { - "escalade": "^3.1.1", - "picocolors": "^1.0.0" - } - }, - "uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "requires": { - "punycode": "^2.1.0" - } - }, - "uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" - }, - "v8-to-istanbul": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.3.tgz", - "integrity": "sha512-9lDD+EVI2fjFsMWXc6dy5JJzBsVTcQ2fVkfBvncZ6xJWG9wtBhOldG+mHkSL0+V1K/xgZz0JDO5UT5hFwHUghg==", - "dev": true, - "requires": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - } - }, - "walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "requires": { - "makeerror": "1.0.12" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "requires": { - "isexe": "^2.0.0" - } - }, - "which-boxed-primitive": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", - "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", - "dev": true, - "requires": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - } - }, - "which-builtin-type": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.1.3.tgz", - "integrity": "sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==", - "dev": true, - "requires": { - "function.prototype.name": "^1.1.5", - "has-tostringtag": "^1.0.0", - "is-async-function": "^2.0.0", - "is-date-object": "^1.0.5", - "is-finalizationregistry": "^1.0.2", - "is-generator-function": "^1.0.10", - "is-regex": "^1.1.4", - "is-weakref": "^1.0.2", - "isarray": "^2.0.5", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" - } - }, - "which-collection": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.1.tgz", - "integrity": "sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==", - "dev": true, - "requires": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" - } - }, - "which-typed-array": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", - "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", - "dev": true, - "requires": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.4", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - } - }, - "wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - } - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" - }, - "write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - } - }, - "y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "requires": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - } - }, - "yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" - } - } -} diff --git a/gh_actions/third_party/no-response/package.json b/gh_actions/third_party/no-response/package.json deleted file mode 100644 index 226d0937a..000000000 --- a/gh_actions/third_party/no-response/package.json +++ /dev/null @@ -1,49 +0,0 @@ -{ - "name": "no-response", - "version": "0.0.0", - "private": true, - "description": "A GitHub Action that closes Issues where the author hasn't responded to a request for more information", - "main": "dist/index.js", - "scripts": { - "build": "npx ncc build ./src/main.ts", - "ci": "npm run format-check && npm run lint && npm test", - "format": "prettier --write **/*.ts **/*.md **/*.yml", - "format-check": "prettier --check **/*.ts **/*.md **/*.yml", - "lint": "npx eslint src/**/*.ts", - "start": "npx ncc run ./src/main.ts", - "test": "jest" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/flutter/cocoon/tree/main/gh_actions/third_party/no-response" - }, - "keywords": [ - "actions", - "node", - "setup" - ], - "author": "lee-dohm", - "license": "MIT", - "dependencies": { - "@actions/core": "^1.10.1", - "@actions/github": "^6.0.0", - "eslint-plugin-prettier": "^5.0.1", - "scramjet": "^4.37.0" - }, - "devDependencies": { - "@octokit/webhooks-types": "^7.3.1", - "@types/jest": "^29.5.8", - "@types/node": "^20.9.0", - "@typescript-eslint/parser": "^6.10.0", - "@vercel/ncc": "^0.38.1", - "eslint": "^8.53.0", - "eslint-plugin-github": "^4.10.1", - "eslint-plugin-jest": "^27.6.0", - "extract-pr-titles": "^1.1.0", - "jest": "^29.7.0", - "js-yaml": "^4.1.0", - "prettier": "3.0.3", - "ts-jest": "^29.1.1", - "typescript": "^5.2.2" - } -} diff --git a/gh_actions/third_party/no-response/src/config.ts b/gh_actions/third_party/no-response/src/config.ts deleted file mode 100644 index 3d351873c..000000000 --- a/gh_actions/third_party/no-response/src/config.ts +++ /dev/null @@ -1,66 +0,0 @@ -import * as core from '@actions/core' -import * as github from '@actions/github' - -const defaultCloseComment = ` -This issue has been automatically closed because there has been no response -to our request for more information from the original author. With only the -information that is currently in the issue, we don't have enough information -to take action. Please reach out if you have or find the answers we need so -that we can investigate further. -` - -interface Repo { - owner: string - repo: string -} - -/** - * Reads, interprets, and encapsulates the configuration for the current run of the Action. - */ -export default class Config { - /** Comment to use when closing an issue, if any. */ - closeComment: string | undefined - - /** How old an issue should be in days before it gets closed. */ - daysUntilClose: number - - /** Repository to operate on. */ - repo: Repo - - /** Color to use when creating the label, encoded as a hex string. */ - responseRequiredColor: string - - /** Name of the label to use for issues that need more information or clarification. */ - responseRequiredLabel: string - - /** GitHub token to use when performing API operations. */ - token: string - - constructor() { - this.closeComment = this.valueOrDefault(core.getInput('closeComment'), defaultCloseComment) - - if (this.closeComment === 'false') { - this.closeComment = undefined - } - - this.daysUntilClose = parseInt(this.valueOrDefault(core.getInput('daysUntilClose'), '14')) - - this.repo = github.context.repo - - this.responseRequiredColor = this.valueOrDefault( - core.getInput('responseRequiredColor'), - 'ffffff' - ) - - this.responseRequiredLabel = this.valueOrDefault( - core.getInput('responseRequiredLabel'), - 'more-information-needed' - ) - - this.token = core.getInput('token', { required: true }) - } - - valueOrDefault(value: string, defaultValue: string): string { - return value !== '' ? value : defaultValue - } -} diff --git a/gh_actions/third_party/no-response/src/main.ts b/gh_actions/third_party/no-response/src/main.ts deleted file mode 100644 index c3865233e..000000000 --- a/gh_actions/third_party/no-response/src/main.ts +++ /dev/null @@ -1,25 +0,0 @@ -import * as core from '@actions/core' - -import Config from './config' -import NoResponse from './no-response' - -async function run(): Promise { - try { - const eventName = process.env['GITHUB_EVENT_NAME'] - - const config = new Config() - const noResponse = new NoResponse(config) - - if (eventName === 'schedule') { - noResponse.sweep() - } else if (eventName === 'issue_comment') { - noResponse.unmark() - } else { - core.error(`Unrecognized event: ${eventName}`) - } - } catch (error) { - core.setFailed(error.message) - } -} - -run() diff --git a/gh_actions/third_party/no-response/src/no-response.ts b/gh_actions/third_party/no-response/src/no-response.ts deleted file mode 100644 index 7aae71f77..000000000 --- a/gh_actions/third_party/no-response/src/no-response.ts +++ /dev/null @@ -1,198 +0,0 @@ -import * as core from '@actions/core' -import * as fs from 'fs' -import * as github from '@actions/github' -import * as scramjet from 'scramjet' - -import Config from './config' -import { GitHub } from '@actions/github/lib/utils' - -/* eslint-disable import/no-unresolved */ -import { IssueCommentEvent } from '@octokit/webhooks-types' -import { RequestInterface } from '@octokit/types' -/* eslint-enable */ - -const fsp = fs.promises - -interface Issue { - issue_number: number - owner: string - repo: string -} - -interface LabeledEvent { - created_at: number - event: string - label: { - name: string - } -} - -interface RestIssue { - number: number -} - -export default class NoResponse { - config: Config - octokit: InstanceType - - constructor(config: Config) { - this.config = config - this.octokit = github.getOctokit(this.config.token) - } - - async sweep(): Promise { - core.debug('Starting sweep') - - await this.ensureLabelExists() - - const issues = await this.getCloseableIssues() - - for (const issue of issues) { - this.close({ issue_number: issue.number, ...this.config.repo }) - } - } - - async unmark(): Promise { - core.debug('Starting unmark') - - const { responseRequiredLabel } = this.config - const payload: IssueCommentEvent = await this.readPayload() - const owner = payload.repository.owner.login - const repo = payload.repository.name - const { number } = payload.issue - const comment = payload.comment - const issue = { owner, repo, issue_number: number } - - const issueInfo = await this.octokit.rest.issues.get(issue) - const isMarked = await this.hasResponseRequiredLabel(issue) - - if (isMarked && issueInfo.data.user?.login === comment.user.login) { - core.info(`${owner}/${repo}#${number} is being unmarked`) - - await this.octokit.rest.issues.removeLabel({ - owner, - repo, - issue_number: number, - name: responseRequiredLabel - }) - - if ( - issueInfo.data.state === 'closed' && - issueInfo.data.user.login !== issueInfo.data.closed_by?.login - ) { - this.octokit.rest.issues.update({ owner, repo, issue_number: number, state: 'open' }) - } - } - } - - async close(issue: Issue): Promise { - const { closeComment } = this.config - - core.info(`${issue.owner}/${issue.repo}#${issue.issue_number} is being closed`) - - if (closeComment) { - await this.octokit.rest.issues.createComment({ body: closeComment, ...issue }) - } - - await this.octokit.rest.issues.update({ state: 'closed', ...issue }) - } - - async ensureLabelExists(): Promise { - try { - await this.octokit.rest.issues.getLabel({ - name: this.config.responseRequiredLabel, - ...this.config.repo - }) - } catch (e) { - this.octokit.rest.issues.createLabel({ - name: this.config.responseRequiredLabel, - color: this.config.responseRequiredColor, - ...this.config.repo - }) - } - } - - async findLastLabeledEvent(issue: Issue): Promise { - const { responseRequiredLabel } = this.config - const events: LabeledEvent[] = await this.octokit.paginate( - (await this.octokit.rest.issues.listEvents({ - ...issue, - per_page: 100 - })) as unknown as RequestInterface - ) - - return events - .reverse() - .find((event) => event.event === 'labeled' && event.label.name === responseRequiredLabel) - } - - async getCloseableIssues(): Promise { - const { owner, repo } = this.config.repo - const { daysUntilClose, responseRequiredLabel } = this.config - const q = `repo:${owner}/${repo} is:issue is:open label:"${responseRequiredLabel}"` - const labeledEarlierThan = this.since(daysUntilClose) - - const issues = await this.octokit.rest.search.issuesAndPullRequests({ - q, - sort: 'updated', - order: 'asc', - per_page: 30 - }) - - core.debug(`Issues to check for closing:`) - core.debug(JSON.stringify(issues, null, 2)) - - const closableIssues = await scramjet - .fromArray(issues.data.items) - .filter(async (issue) => { - const event = await this.findLastLabeledEvent({ - issue_number: issue.number, - ...this.config.repo - }) - - if (!event) { - return false - } - - core.debug(`Checking: ${JSON.stringify(issue, null, 2)}`) - core.debug(`Using: ${JSON.stringify(event, null, 2)}`) - - const creationDate = new Date(event.created_at) - - core.debug( - `${creationDate.toISOString()} < ${labeledEarlierThan.toISOString()} === ${ - creationDate < labeledEarlierThan - }` - ) - - return creationDate < labeledEarlierThan - }) - .toArray() - - core.debug(`Closeable: ${JSON.stringify(closableIssues, null, 2)}`) - - return closableIssues - } - - async hasResponseRequiredLabel(issue: Issue): Promise { - const labels = await this.octokit.rest.issues.listLabelsOnIssue({ ...issue }) - - return labels.data.map((label) => label.name).includes(this.config.responseRequiredLabel) - } - - async readPayload(): Promise { - if (!process.env.GITHUB_EVENT_PATH) { - throw new Error('GITHUB_EVENT_PATH is not defined') - } - - const text = (await fsp.readFile(process.env.GITHUB_EVENT_PATH)).toString() - - return JSON.parse(text) - } - - since(days: number): Date { - const ttl = days * 24 * 60 * 60 * 1000 - - return new Date(new Date().getTime() - ttl) - } -} diff --git a/gh_actions/third_party/no-response/test/config.test.ts b/gh_actions/third_party/no-response/test/config.test.ts deleted file mode 100644 index 4211ba938..000000000 --- a/gh_actions/third_party/no-response/test/config.test.ts +++ /dev/null @@ -1,96 +0,0 @@ -import * as core from '@actions/core' - -import Config from '../src/config' - -describe('Config', () => { - describe('constructor', () => { - beforeEach(() => { - process.env['INPUT_TOKEN'] = '123456789abcdef' - process.env['GITHUB_REPOSITORY'] = 'test-owner/test-repo' - }) - - it('initializes closeComment to input value', () => { - process.env['INPUT_CLOSECOMMENT'] = 'foo' - const config = new Config() - - expect(config.closeComment).toEqual('foo') - }) - - it('initializes closeComment as undefined if "false" is passed in as input', () => { - process.env['INPUT_CLOSECOMMENT'] = 'false' - const config = new Config() - - expect(config.closeComment).toBeUndefined() - }) - - it('initializes closeComment with the default otherwise', () => { - delete process.env.INPUT_CLOSECOMMENT - const config = new Config() - - expect(config.closeComment).toContain('This issue has been automatically closed') - }) - - it('initializes daysUntilClose with the integer value of the input', () => { - process.env['INPUT_DAYSUNTILCLOSE'] = '100' - const config = new Config() - - expect(config.daysUntilClose).toEqual(100) - }) - - it('initializes daysUntilClose with the default otherwise', () => { - delete process.env.INPUT_DAYSUNTILCLOSE - const config = new Config() - - expect(config.daysUntilClose).toEqual(14) - }) - - it('initializes repo with the repository information', () => { - const config = new Config() - - expect(config.repo.owner).toEqual('test-owner') - expect(config.repo.repo).toEqual('test-repo') - }) - - it('initializes responseRequiredColor with the value of the input', () => { - process.env['INPUT_RESPONSEREQUIREDCOLOR'] = '000000' - const config = new Config() - - expect(config.responseRequiredColor).toEqual('000000') - }) - - it('initializes responseRequiredColor with the default otherwise', () => { - delete process.env.INPUT_RESPONSEREQUIREDCOLOR - const config = new Config() - - expect(config.responseRequiredColor).toEqual('ffffff') - }) - - it('initializes responseRequiredLabel with the value of the input', () => { - process.env['INPUT_RESPONSEREQUIREDLABEL'] = 'label-name' - const config = new Config() - - expect(config.responseRequiredLabel).toEqual('label-name') - }) - - it('initializes responseRequiredLabel with the default otherwise', () => { - delete process.env.INPUT_RESPONSEREQUIREDLABEL - const config = new Config() - - expect(config.responseRequiredLabel).toEqual('more-information-needed') - }) - - it('initializes token with the value of the input', () => { - const config = new Config() - - expect(config.token).toEqual('123456789abcdef') - }) - - it('raises an error if no token is given', () => { - delete process.env.INPUT_TOKEN - - expect(() => { - new Config() - }).toThrow() - }) - }) -}) diff --git a/gh_actions/third_party/no-response/tsconfig.json b/gh_actions/third_party/no-response/tsconfig.json deleted file mode 100644 index a62cdc605..000000000 --- a/gh_actions/third_party/no-response/tsconfig.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "compilerOptions": { - "target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ - "outDir": "./lib", /* Redirect output structure to the directory. */ - "rootDir": "./src", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ - "strict": true, /* Enable all strict type-checking options. */ - "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ - "moduleResolution": "node", - "useUnknownInCatchVariables": false - }, - "exclude": ["node_modules", "**/*.test.ts"] -} diff --git a/licenses/README.md b/licenses/README.md deleted file mode 100644 index 940a9076a..000000000 --- a/licenses/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Licenses - -Test utility for verifying LICENSES and headers are in compliance. - -To run on Cocoon, do `dart check_licenses.dart` \ No newline at end of file diff --git a/licenses/check_licenses.dart b/licenses/check_licenses.dart deleted file mode 100644 index 429b1e8c5..000000000 --- a/licenses/check_licenses.dart +++ /dev/null @@ -1,277 +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:path/path.dart' as path; -import 'dart:io' as io_internals show exit; - -final bool hasColor = stdout.supportsAnsiEscapes; -final String bold = hasColor ? '\x1B[1m' : ''; // used for shard titles -final String red = hasColor ? '\x1B[31m' : ''; // used for errors -final String reset = hasColor ? '\x1B[0m' : ''; -final String reverse = hasColor ? '\x1B[7m' : ''; // used for clocks - -Future main() async { - print('$clock STARTING ANALYSIS'); - try { - await run(); - } on ExitException catch (error) { - error.apply(); - } - print('$clock ${bold}Analysis successful.$reset'); -} - -Future run() async { - final String cocoonPath = path.join(path.dirname(Platform.script.path), '..'); - print('$clock Root path: $cocoonPath'); - print('$clock Licenses...'); - await verifyConsistentLicenses(cocoonPath); - await verifyNoMissingLicense(cocoonPath); -} - -// TESTS -String _generateLicense(String prefix) { - return '${prefix}Copyright (2014|2015|2016|2017|2018|2019|2020|2021|2022|2023) The Flutter Authors. All rights reserved.\n' - '${prefix}Use of this source code is governed by a BSD-style license that can be\n' - '${prefix}found in the LICENSE file.'; -} - -/// Ensure that LICENSES in Cocoon and its packages are consistent with each other. -/// -/// Verifies that every LICENSE file in Cocoon matches cocoon/LICENSE. -Future verifyConsistentLicenses(String workingDirectory) async { - final String goldenLicensePath = '$workingDirectory/LICENSE'; - final String goldenLicense = File(goldenLicensePath).readAsStringSync(); - if (goldenLicense.isEmpty) { - throw Exception('No LICENSE was found at the root of Cocoon'); - } - - final List badLicenses = []; - for (final FileSystemEntity entity in Directory(workingDirectory).listSync(recursive: true)) { - final String cocoonPath = entity.path.split('/../').last; - if (cocoonPath.contains(RegExp('(\.git)|(\.dart_tool)|(\.plugin_symlinks)'))) { - continue; - } - - if (path.basename(entity.path) == 'LICENSE') { - final String license = File(entity.path).readAsStringSync(); - if (license != goldenLicense) { - badLicenses.add(cocoonPath); - } - } - } - - if (badLicenses.isNotEmpty) { - exitWithError( - ['The following LICENSE files do not match the golden LICENSE at root:']..insertAll(1, badLicenses), - ); - } -} - -Future verifyNoMissingLicense(String workingDirectory, {bool checkMinimums = true}) async { - final int? overrideMinimumMatches = checkMinimums ? null : 0; - await _verifyNoMissingLicenseForExtension( - workingDirectory, - 'dart', - overrideMinimumMatches ?? 2000, - _generateLicense('// '), - ); - await _verifyNoMissingLicenseForExtension( - workingDirectory, - 'java', - overrideMinimumMatches ?? 39, - _generateLicense('// '), - ); - await _verifyNoMissingLicenseForExtension( - workingDirectory, - 'h', - overrideMinimumMatches ?? 30, - _generateLicense('// '), - ); - await _verifyNoMissingLicenseForExtension( - workingDirectory, - 'm', - overrideMinimumMatches ?? 30, - _generateLicense('// '), - ); - await _verifyNoMissingLicenseForExtension( - workingDirectory, - 'swift', - overrideMinimumMatches ?? 10, - _generateLicense('// '), - ); - await _verifyNoMissingLicenseForExtension( - workingDirectory, - 'gradle', - overrideMinimumMatches ?? 100, - _generateLicense('// '), - ); - await _verifyNoMissingLicenseForExtension( - workingDirectory, - 'gn', - overrideMinimumMatches ?? 0, - _generateLicense('# '), - ); - await _verifyNoMissingLicenseForExtension( - workingDirectory, - 'Dockerfile', - overrideMinimumMatches ?? 1, - _generateLicense('# '), - ); - await _verifyNoMissingLicenseForExtension( - workingDirectory, - 'sh', - overrideMinimumMatches ?? 1, - '#![^\n]+sh\n' + _generateLicense('# '), - ); - await _verifyNoMissingLicenseForExtension( - workingDirectory, - 'bat', - overrideMinimumMatches ?? 1, - _generateLicense(':: '), - ); - await _verifyNoMissingLicenseForExtension( - workingDirectory, - 'ps1', - overrideMinimumMatches ?? 1, - _generateLicense('# '), - ); - await _verifyNoMissingLicenseForExtension( - workingDirectory, - 'html', - overrideMinimumMatches ?? 1, - '', - trailingBlank: false, - ); - await _verifyNoMissingLicenseForExtension( - workingDirectory, - 'xml', - overrideMinimumMatches ?? 1, - '', - ); -} - -Future _verifyNoMissingLicenseForExtension( - String workingDirectory, - String extension, - int minimumMatches, - String license, { - bool trailingBlank = true, -}) async { - assert(!license.endsWith('\n')); - final String licensePattern = license + '\n' + (trailingBlank ? '\n' : ''); - final List errors = []; - for (final File file in _allFiles(workingDirectory, extension, minimumMatches: minimumMatches)) { - final String contents = file.readAsStringSync().replaceAll('\r\n', '\n'); - if (contents.isEmpty) continue; // let's not go down the /bin/true rabbit hole - if (!contents.startsWith(RegExp(licensePattern))) errors.add(file.path); - } - // Fail if any errors - if (errors.isNotEmpty) { - final String s = errors.length == 1 ? ' does' : 's do'; - exitWithError([ - '${bold}The following ${errors.length} file$s not have the right license header:$reset', - ...errors, - 'The expected license header is:', - license, - if (trailingBlank) '...followed by a blank line.', - ]); - } -} - -Iterable _allFiles(String workingDirectory, String extension, {required int minimumMatches}) sync* { - assert(!extension.startsWith('.'), 'Extension argument should not start with a period.'); - final Set pending = {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 (_isGeneratedPluginRegistrant(entity)) continue; - if (path.basename(entity.path) == 'AppDelegate.h') continue; - if (path.basename(entity.path) == 'flutter_export_environment.sh') continue; - if (path.basename(entity.path) == 'gradlew.bat') continue; - if (path.basename(entity.path) == 'Runner-Bridging-Header.h') continue; - if (path.basename(entity.path).endsWith('g.dart')) continue; - if (path.basename(entity.path).endsWith('mocks.mocks.dart')) continue; - if (path.basename(entity.path).endsWith('pb.dart')) continue; - if (path.basename(entity.path).endsWith('pbenum.dart')) continue; - if (path.basename(entity.path).endsWith('pbjson.dart')) continue; - if (path.basename(entity.path).endsWith('pbgrpc.dart')) continue; - if (path.basename(entity.path).endsWith('pbserver.dart')) continue; - if (path.extension(entity.path) == '.$extension') { - matches += 1; - yield entity; - } - if (path.basename(entity.path) == 'Dockerfile' && extension == 'Dockerfile') { - matches += 1; - yield entity; - } - } else if (entity is Directory) { - if (File(path.join(entity.path, '.dartignore')).existsSync()) continue; - if (path.basename(entity.path) == '.git') continue; - if (path.basename(entity.path) == '.gradle') continue; - if (path.basename(entity.path) == '.dart_tool') continue; - if (path.basename(entity.path) == 'third_party') continue; - if (_isPartOfAppTemplate(entity)) continue; - pending.addAll(entity.listSync()); - } - } - assert( - matches >= minimumMatches, - 'Expected to find at least $minimumMatches files with extension ".$extension" in "$workingDirectory", but only found $matches.', - ); -} - -bool _isPartOfAppTemplate(Directory directory) { - const Set templateDirs = { - 'android', - 'build', - 'ios', - 'linux', - 'macos', - 'web', - 'windows', - }; - // Project directories will have a metadata file in them. - if (File(path.join(directory.parent.path, '.metadata')).existsSync()) { - return templateDirs.contains(path.basename(directory.path)); - } - return false; -} - -bool _isGeneratedPluginRegistrant(File file) { - final String filename = path.basenameWithoutExtension(file.path); - return !path.split(file.path).contains('.pub-cache') && - (filename == 'generated_plugin_registrant' || filename == 'GeneratedPluginRegistrant'); -} - -void exitWithError(List messages) { - final String redLine = '$red━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━$reset'; - print(redLine); - messages.forEach(print); - print(redLine); - exit(1); -} - -class ExitException implements Exception { - ExitException(this.exitCode); - - final int exitCode; - - void apply() { - io_internals.exit(exitCode); - } -} - -String get clock { - final DateTime now = DateTime.now(); - return '$reverse▌' - '${now.hour.toString().padLeft(2, "0")}:' - '${now.minute.toString().padLeft(2, "0")}:' - '${now.second.toString().padLeft(2, "0")}' - '▐$reset'; -} diff --git a/licenses/pubspec.yaml b/licenses/pubspec.yaml deleted file mode 100644 index 39f954358..000000000 --- a/licenses/pubspec.yaml +++ /dev/null @@ -1,13 +0,0 @@ -name: license_checks -description: Script to check licenses. - -environment: - sdk: ">=2.18.0 <4.0.0" - -dependencies: - path: 1.8.3 - platform: 3.1.3 - -dev_dependencies: - mockito: 5.4.2 - test_api: 0.6.1 diff --git a/packages/buildbucket-dart/.gitignore b/packages/buildbucket-dart/.gitignore deleted file mode 100644 index db563d571..000000000 --- a/packages/buildbucket-dart/.gitignore +++ /dev/null @@ -1,13 +0,0 @@ -# Files and directories created by pub. -.dart_tool/ -.packages - -# Conventional directory for build outputs. -build/ - -# Omit committing pubspec.lock for library packages; see -# https://dart.dev/guides/libraries/private-files#pubspeclock. -pubspec.lock - -# Directory used to checkout proto deps to process them. -buildbucket_tmp diff --git a/packages/buildbucket-dart/CHANGELOG.md b/packages/buildbucket-dart/CHANGELOG.md deleted file mode 100644 index 5ebc12421..000000000 --- a/packages/buildbucket-dart/CHANGELOG.md +++ /dev/null @@ -1,21 +0,0 @@ -# 1.0.6 - -- Expose all objects in build, builds_service and builder_service. - -# 1.0.5 - -- Expose build bucket clients and supporting fields like StringPair. - -# 1.0.4 - -- Expose status enums and related status fields from the Build proto. - -# 1.0.1 - -- Update required Dart revisions for sound null safety. -- Add the notification proto for buildbucket v2. - - -# 1.0.0 - -- Initial version generating dart code for Buildbucket protos. diff --git a/packages/buildbucket-dart/LICENSE b/packages/buildbucket-dart/LICENSE deleted file mode 100644 index d5384ca42..000000000 --- a/packages/buildbucket-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/packages/buildbucket-dart/README.md b/packages/buildbucket-dart/README.md deleted file mode 100644 index 3fa2a19d0..000000000 --- a/packages/buildbucket-dart/README.md +++ /dev/null @@ -1,19 +0,0 @@ -# Dart Buildbucket - -Dart LUCI buildbucket protobufs. - -## Details - -These protobufs are used to communicate with LUCI Buildbucket services from Dart Language. - -## Regenerating the protos - -* Run `dart pub global activate protoc_plugin`. -* From `packages/buildbucket-dart` run `bash tool/regenerate.sh`. - -That will checkout protobuf, buildbucket and googleapis repositories. It will also compile the protos -and generate their correspondent Dart classes. - -## Feedback - -File an issue in flutter/flutter with the word 'buildbucket-dart' clearly in the title and cc @godofredoc. diff --git a/packages/buildbucket-dart/analysis_options.yaml b/packages/buildbucket-dart/analysis_options.yaml deleted file mode 100644 index eddf03ff3..000000000 --- a/packages/buildbucket-dart/analysis_options.yaml +++ /dev/null @@ -1,30 +0,0 @@ -# This file configures the static analysis results for your project (errors, -# warnings, and lints). -# -# This enables the 'recommended' set of lints from `package:lints`. -# This set helps identify many issues that may lead to problems when running -# or consuming Dart code, and enforces writing Dart using a single, idiomatic -# style and format. -# -# If you want a smaller set of lints you can change this to specify -# 'package:lints/core.yaml'. These are just the most critical lints -# (the recommended set includes the core lints). -# The core lints are also what is used by pub.dev for scoring packages. - -include: package:lints/recommended.yaml - -# Uncomment the following section to specify additional rules. - -# linter: -# rules: -# - camel_case_types - -analyzer: - exclude: - - lib/src/generated/** - -# For more information about the core and recommended set of lints, see -# https://dart.dev/go/core-lints - -# For additional information about configuring this file, see -# https://dart.dev/guides/language/analysis-options diff --git a/packages/buildbucket-dart/example/buildbucket_pb_example.dart b/packages/buildbucket-dart/example/buildbucket_pb_example.dart deleted file mode 100644 index a211f04ad..000000000 --- a/packages/buildbucket-dart/example/buildbucket_pb_example.dart +++ /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. - -import 'dart:convert'; - -import 'package:buildbucket/buildbucket_pb.dart'; - -void main() { - // Create a BuildBucket build from a json string. - Build build = Build(); - final String json = '{"builder": {"project": "flutter", "bucket": "try", "builder": "buildabc"}}'; - Map jsonObject = jsonDecode(json); - build.mergeFromProto3Json(jsonObject); - // Enconding the Build instance to a json object. - print(build.toProto3Json()); -} diff --git a/packages/buildbucket-dart/lib/buildbucket_pb.dart b/packages/buildbucket-dart/lib/buildbucket_pb.dart deleted file mode 100644 index 7a67646f2..000000000 --- a/packages/buildbucket-dart/lib/buildbucket_pb.dart +++ /dev/null @@ -1,80 +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. - -library buildbucket; - -export 'src/generated/go.chromium.org/luci/buildbucket/proto/build.pb.dart' - show - Build, - BuildInfra, - BuildInfra_BBAgent, - BuildInfra_BBAgent_Input, - BuildInfra_BBAgent_Input_CIPDPackage, - BuildInfra_Backend, - BuildInfra_Buildbucket, - BuildInfra_Buildbucket_Agent, - BuildInfra_Buildbucket_Agent_Input, - BuildInfra_Buildbucket_Agent_Output, - BuildInfra_Buildbucket_Agent_Purpose, - BuildInfra_Buildbucket_Agent_Source, - BuildInfra_Buildbucket_Agent_Source_CIPD, - BuildInfra_Buildbucket_ExperimentReason, - BuildInfra_LogDog, - BuildInfra_Recipe, - BuildInfra_ResultDB, - BuildInfra_Swarming, - BuildInfra_Swarming_CacheEntry, - Build_BuilderInfo, - Build_Input, - Build_Output, - BuildInfra_Buildbucket_Agent_Source_DataType; -export 'src/generated/go.chromium.org/luci/buildbucket/proto/builds_service.pb.dart' - show - GetBuildRequest, - SearchBuildsRequest, - SearchBuildsResponse, - BatchRequest, - BatchRequest_Request, - BatchResponse, - BatchResponse_Response, - BatchRequest_Request_Request, - BatchResponse_Response_Response, - UpdateBuildRequest, - ScheduleBuildRequest, - ScheduleBuildRequest_ShadowInput, - ScheduleBuildRequest_Swarming, - StartBuildRequest, - StartBuildResponse, - StartBuildTaskRequest, - StartBuildTaskResponse, - CancelBuildRequest, - CreateBuildRequest, - SynthesizeBuildRequest, - BuildMask, - BuildPredicate, - BuildRange, - GetBuildStatusRequest; -export 'src/generated/go.chromium.org/luci/buildbucket/proto/builder_service.pb.dart' - show GetBuilderRequest, ListBuildersRequest, ListBuildersResponse; -export 'src/generated/go.chromium.org/luci/buildbucket/proto/task.pb.dart' show Task, TaskID; -export 'src/generated/go.chromium.org/luci/buildbucket/proto/builder_common.pb.dart' show BuilderID; -export 'src/generated/go.chromium.org/luci/buildbucket/proto/common.pb.dart' - show - Status, - StatusDetails, - StatusDetails_ResourceExhaustion, - StatusDetails_Timeout, - StringPair, - RequestedDimension, - Trinary, - TimeRange, - Compression, - HealthStatus, - CacheEntry, - GitilesCommit, - GerritChange, - Executable; -export 'src/generated/go.chromium.org/luci/buildbucket/proto/common.pbenum.dart'; -export 'src/generated/go.chromium.org/luci/buildbucket/proto/notification.pb.dart' - show NotificationConfig, BuildsV2PubSub, PubSubCallBack; diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/build.pb.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/build.pb.dart deleted file mode 100644 index 558c63fe5..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/build.pb.dart +++ /dev/null @@ -1,2777 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/build.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:core' as $core; - -import 'package:fixnum/fixnum.dart' as $fixnum; -import 'package:protobuf/protobuf.dart' as $pb; - -import '../../../../google/protobuf/duration.pb.dart' as $4; -import '../../../../google/protobuf/struct.pb.dart' as $5; -import '../../../../google/protobuf/timestamp.pb.dart' as $1; -import '../../resultdb/proto/v1/invocation.pb.dart' as $6; -import 'build.pbenum.dart'; -import 'builder_common.pb.dart' as $0; -import 'common.pb.dart' as $3; -import 'common.pbenum.dart' as $3; -import 'step.pb.dart' as $2; -import 'task.pb.dart' as $7; - -export 'build.pbenum.dart'; - -class Build_Input extends $pb.GeneratedMessage { - factory Build_Input() => create(); - Build_Input._() : super(); - factory Build_Input.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory Build_Input.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Build.Input', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOM<$5.Struct>(1, _omitFieldNames ? '' : 'properties', subBuilder: $5.Struct.create) - ..aOM<$3.GitilesCommit>(2, _omitFieldNames ? '' : 'gitilesCommit', subBuilder: $3.GitilesCommit.create) - ..pc<$3.GerritChange>(3, _omitFieldNames ? '' : 'gerritChanges', $pb.PbFieldType.PM, - subBuilder: $3.GerritChange.create) - ..aOB(5, _omitFieldNames ? '' : 'experimental') - ..pPS(6, _omitFieldNames ? '' : 'experiments') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - Build_Input clone() => Build_Input()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - Build_Input copyWith(void Function(Build_Input) updates) => - super.copyWith((message) => updates(message as Build_Input)) as Build_Input; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static Build_Input create() => Build_Input._(); - Build_Input createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Build_Input getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Build_Input? _defaultInstance; - - @$pb.TagNumber(1) - $5.Struct get properties => $_getN(0); - @$pb.TagNumber(1) - set properties($5.Struct v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasProperties() => $_has(0); - @$pb.TagNumber(1) - void clearProperties() => clearField(1); - @$pb.TagNumber(1) - $5.Struct ensureProperties() => $_ensure(0); - - @$pb.TagNumber(2) - $3.GitilesCommit get gitilesCommit => $_getN(1); - @$pb.TagNumber(2) - set gitilesCommit($3.GitilesCommit v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasGitilesCommit() => $_has(1); - @$pb.TagNumber(2) - void clearGitilesCommit() => clearField(2); - @$pb.TagNumber(2) - $3.GitilesCommit ensureGitilesCommit() => $_ensure(1); - - @$pb.TagNumber(3) - $core.List<$3.GerritChange> get gerritChanges => $_getList(2); - - @$pb.TagNumber(5) - $core.bool get experimental => $_getBF(3); - @$pb.TagNumber(5) - set experimental($core.bool v) { - $_setBool(3, v); - } - - @$pb.TagNumber(5) - $core.bool hasExperimental() => $_has(3); - @$pb.TagNumber(5) - void clearExperimental() => clearField(5); - - @$pb.TagNumber(6) - $core.List<$core.String> get experiments => $_getList(4); -} - -class Build_Output extends $pb.GeneratedMessage { - factory Build_Output() => create(); - Build_Output._() : super(); - factory Build_Output.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory Build_Output.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Build.Output', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOM<$5.Struct>(1, _omitFieldNames ? '' : 'properties', subBuilder: $5.Struct.create) - ..aOS(2, _omitFieldNames ? '' : 'summaryMarkdown') - ..aOM<$3.GitilesCommit>(3, _omitFieldNames ? '' : 'gitilesCommit', subBuilder: $3.GitilesCommit.create) - ..pc<$3.Log>(5, _omitFieldNames ? '' : 'logs', $pb.PbFieldType.PM, subBuilder: $3.Log.create) - ..e<$3.Status>(6, _omitFieldNames ? '' : 'status', $pb.PbFieldType.OE, - defaultOrMaker: $3.Status.STATUS_UNSPECIFIED, valueOf: $3.Status.valueOf, enumValues: $3.Status.values) - ..aOM<$3.StatusDetails>(7, _omitFieldNames ? '' : 'statusDetails', subBuilder: $3.StatusDetails.create) - ..aOS(8, _omitFieldNames ? '' : 'summaryHtml') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - Build_Output clone() => Build_Output()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - Build_Output copyWith(void Function(Build_Output) updates) => - super.copyWith((message) => updates(message as Build_Output)) as Build_Output; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static Build_Output create() => Build_Output._(); - Build_Output createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Build_Output getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Build_Output? _defaultInstance; - - @$pb.TagNumber(1) - $5.Struct get properties => $_getN(0); - @$pb.TagNumber(1) - set properties($5.Struct v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasProperties() => $_has(0); - @$pb.TagNumber(1) - void clearProperties() => clearField(1); - @$pb.TagNumber(1) - $5.Struct ensureProperties() => $_ensure(0); - - @$pb.TagNumber(2) - $core.String get summaryMarkdown => $_getSZ(1); - @$pb.TagNumber(2) - set summaryMarkdown($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasSummaryMarkdown() => $_has(1); - @$pb.TagNumber(2) - void clearSummaryMarkdown() => clearField(2); - - @$pb.TagNumber(3) - $3.GitilesCommit get gitilesCommit => $_getN(2); - @$pb.TagNumber(3) - set gitilesCommit($3.GitilesCommit v) { - setField(3, v); - } - - @$pb.TagNumber(3) - $core.bool hasGitilesCommit() => $_has(2); - @$pb.TagNumber(3) - void clearGitilesCommit() => clearField(3); - @$pb.TagNumber(3) - $3.GitilesCommit ensureGitilesCommit() => $_ensure(2); - - @$pb.TagNumber(5) - $core.List<$3.Log> get logs => $_getList(3); - - @$pb.TagNumber(6) - $3.Status get status => $_getN(4); - @$pb.TagNumber(6) - set status($3.Status v) { - setField(6, v); - } - - @$pb.TagNumber(6) - $core.bool hasStatus() => $_has(4); - @$pb.TagNumber(6) - void clearStatus() => clearField(6); - - @$pb.TagNumber(7) - $3.StatusDetails get statusDetails => $_getN(5); - @$pb.TagNumber(7) - set statusDetails($3.StatusDetails v) { - setField(7, v); - } - - @$pb.TagNumber(7) - $core.bool hasStatusDetails() => $_has(5); - @$pb.TagNumber(7) - void clearStatusDetails() => clearField(7); - @$pb.TagNumber(7) - $3.StatusDetails ensureStatusDetails() => $_ensure(5); - - @$pb.TagNumber(8) - $core.String get summaryHtml => $_getSZ(6); - @$pb.TagNumber(8) - set summaryHtml($core.String v) { - $_setString(6, v); - } - - @$pb.TagNumber(8) - $core.bool hasSummaryHtml() => $_has(6); - @$pb.TagNumber(8) - void clearSummaryHtml() => clearField(8); -} - -class Build_BuilderInfo extends $pb.GeneratedMessage { - factory Build_BuilderInfo() => create(); - Build_BuilderInfo._() : super(); - factory Build_BuilderInfo.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory Build_BuilderInfo.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Build.BuilderInfo', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'description') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - Build_BuilderInfo clone() => Build_BuilderInfo()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - Build_BuilderInfo copyWith(void Function(Build_BuilderInfo) updates) => - super.copyWith((message) => updates(message as Build_BuilderInfo)) as Build_BuilderInfo; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static Build_BuilderInfo create() => Build_BuilderInfo._(); - Build_BuilderInfo createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Build_BuilderInfo getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Build_BuilderInfo? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get description => $_getSZ(0); - @$pb.TagNumber(1) - set description($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasDescription() => $_has(0); - @$pb.TagNumber(1) - void clearDescription() => clearField(1); -} - -class Build extends $pb.GeneratedMessage { - factory Build() => create(); - Build._() : super(); - factory Build.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory Build.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Build', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aInt64(1, _omitFieldNames ? '' : 'id') - ..aOM<$0.BuilderID>(2, _omitFieldNames ? '' : 'builder', subBuilder: $0.BuilderID.create) - ..a<$core.int>(3, _omitFieldNames ? '' : 'number', $pb.PbFieldType.O3) - ..aOS(4, _omitFieldNames ? '' : 'createdBy') - ..aOM<$1.Timestamp>(6, _omitFieldNames ? '' : 'createTime', subBuilder: $1.Timestamp.create) - ..aOM<$1.Timestamp>(7, _omitFieldNames ? '' : 'startTime', subBuilder: $1.Timestamp.create) - ..aOM<$1.Timestamp>(8, _omitFieldNames ? '' : 'endTime', subBuilder: $1.Timestamp.create) - ..aOM<$1.Timestamp>(9, _omitFieldNames ? '' : 'updateTime', subBuilder: $1.Timestamp.create) - ..e<$3.Status>(12, _omitFieldNames ? '' : 'status', $pb.PbFieldType.OE, - defaultOrMaker: $3.Status.STATUS_UNSPECIFIED, valueOf: $3.Status.valueOf, enumValues: $3.Status.values) - ..aOM(15, _omitFieldNames ? '' : 'input', subBuilder: Build_Input.create) - ..aOM(16, _omitFieldNames ? '' : 'output', subBuilder: Build_Output.create) - ..pc<$2.Step>(17, _omitFieldNames ? '' : 'steps', $pb.PbFieldType.PM, subBuilder: $2.Step.create) - ..aOM(18, _omitFieldNames ? '' : 'infra', subBuilder: BuildInfra.create) - ..pc<$3.StringPair>(19, _omitFieldNames ? '' : 'tags', $pb.PbFieldType.PM, subBuilder: $3.StringPair.create) - ..aOS(20, _omitFieldNames ? '' : 'summaryMarkdown') - ..e<$3.Trinary>(21, _omitFieldNames ? '' : 'critical', $pb.PbFieldType.OE, - defaultOrMaker: $3.Trinary.UNSET, valueOf: $3.Trinary.valueOf, enumValues: $3.Trinary.values) - ..aOM<$3.StatusDetails>(22, _omitFieldNames ? '' : 'statusDetails', subBuilder: $3.StatusDetails.create) - ..aOS(23, _omitFieldNames ? '' : 'canceledBy') - ..aOM<$3.Executable>(24, _omitFieldNames ? '' : 'exe', subBuilder: $3.Executable.create) - ..aOB(25, _omitFieldNames ? '' : 'canary') - ..aOM<$4.Duration>(26, _omitFieldNames ? '' : 'schedulingTimeout', subBuilder: $4.Duration.create) - ..aOM<$4.Duration>(27, _omitFieldNames ? '' : 'executionTimeout', subBuilder: $4.Duration.create) - ..aOB(28, _omitFieldNames ? '' : 'waitForCapacity') - ..aOM<$4.Duration>(29, _omitFieldNames ? '' : 'gracePeriod', subBuilder: $4.Duration.create) - ..aOB(30, _omitFieldNames ? '' : 'canOutliveParent') - ..p<$fixnum.Int64>(31, _omitFieldNames ? '' : 'ancestorIds', $pb.PbFieldType.K6) - ..aOM<$1.Timestamp>(32, _omitFieldNames ? '' : 'cancelTime', subBuilder: $1.Timestamp.create) - ..aOS(33, _omitFieldNames ? '' : 'cancellationMarkdown') - ..aOM(34, _omitFieldNames ? '' : 'builderInfo', subBuilder: Build_BuilderInfo.create) - ..e<$3.Trinary>(35, _omitFieldNames ? '' : 'retriable', $pb.PbFieldType.OE, - defaultOrMaker: $3.Trinary.UNSET, valueOf: $3.Trinary.valueOf, enumValues: $3.Trinary.values) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - Build clone() => Build()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - Build copyWith(void Function(Build) updates) => super.copyWith((message) => updates(message as Build)) as Build; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static Build create() => Build._(); - Build createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Build getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Build? _defaultInstance; - - @$pb.TagNumber(1) - $fixnum.Int64 get id => $_getI64(0); - @$pb.TagNumber(1) - set id($fixnum.Int64 v) { - $_setInt64(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasId() => $_has(0); - @$pb.TagNumber(1) - void clearId() => clearField(1); - - @$pb.TagNumber(2) - $0.BuilderID get builder => $_getN(1); - @$pb.TagNumber(2) - set builder($0.BuilderID v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasBuilder() => $_has(1); - @$pb.TagNumber(2) - void clearBuilder() => clearField(2); - @$pb.TagNumber(2) - $0.BuilderID ensureBuilder() => $_ensure(1); - - @$pb.TagNumber(3) - $core.int get number => $_getIZ(2); - @$pb.TagNumber(3) - set number($core.int v) { - $_setSignedInt32(2, v); - } - - @$pb.TagNumber(3) - $core.bool hasNumber() => $_has(2); - @$pb.TagNumber(3) - void clearNumber() => clearField(3); - - @$pb.TagNumber(4) - $core.String get createdBy => $_getSZ(3); - @$pb.TagNumber(4) - set createdBy($core.String v) { - $_setString(3, v); - } - - @$pb.TagNumber(4) - $core.bool hasCreatedBy() => $_has(3); - @$pb.TagNumber(4) - void clearCreatedBy() => clearField(4); - - @$pb.TagNumber(6) - $1.Timestamp get createTime => $_getN(4); - @$pb.TagNumber(6) - set createTime($1.Timestamp v) { - setField(6, v); - } - - @$pb.TagNumber(6) - $core.bool hasCreateTime() => $_has(4); - @$pb.TagNumber(6) - void clearCreateTime() => clearField(6); - @$pb.TagNumber(6) - $1.Timestamp ensureCreateTime() => $_ensure(4); - - @$pb.TagNumber(7) - $1.Timestamp get startTime => $_getN(5); - @$pb.TagNumber(7) - set startTime($1.Timestamp v) { - setField(7, v); - } - - @$pb.TagNumber(7) - $core.bool hasStartTime() => $_has(5); - @$pb.TagNumber(7) - void clearStartTime() => clearField(7); - @$pb.TagNumber(7) - $1.Timestamp ensureStartTime() => $_ensure(5); - - @$pb.TagNumber(8) - $1.Timestamp get endTime => $_getN(6); - @$pb.TagNumber(8) - set endTime($1.Timestamp v) { - setField(8, v); - } - - @$pb.TagNumber(8) - $core.bool hasEndTime() => $_has(6); - @$pb.TagNumber(8) - void clearEndTime() => clearField(8); - @$pb.TagNumber(8) - $1.Timestamp ensureEndTime() => $_ensure(6); - - @$pb.TagNumber(9) - $1.Timestamp get updateTime => $_getN(7); - @$pb.TagNumber(9) - set updateTime($1.Timestamp v) { - setField(9, v); - } - - @$pb.TagNumber(9) - $core.bool hasUpdateTime() => $_has(7); - @$pb.TagNumber(9) - void clearUpdateTime() => clearField(9); - @$pb.TagNumber(9) - $1.Timestamp ensureUpdateTime() => $_ensure(7); - - @$pb.TagNumber(12) - $3.Status get status => $_getN(8); - @$pb.TagNumber(12) - set status($3.Status v) { - setField(12, v); - } - - @$pb.TagNumber(12) - $core.bool hasStatus() => $_has(8); - @$pb.TagNumber(12) - void clearStatus() => clearField(12); - - @$pb.TagNumber(15) - Build_Input get input => $_getN(9); - @$pb.TagNumber(15) - set input(Build_Input v) { - setField(15, v); - } - - @$pb.TagNumber(15) - $core.bool hasInput() => $_has(9); - @$pb.TagNumber(15) - void clearInput() => clearField(15); - @$pb.TagNumber(15) - Build_Input ensureInput() => $_ensure(9); - - @$pb.TagNumber(16) - Build_Output get output => $_getN(10); - @$pb.TagNumber(16) - set output(Build_Output v) { - setField(16, v); - } - - @$pb.TagNumber(16) - $core.bool hasOutput() => $_has(10); - @$pb.TagNumber(16) - void clearOutput() => clearField(16); - @$pb.TagNumber(16) - Build_Output ensureOutput() => $_ensure(10); - - @$pb.TagNumber(17) - $core.List<$2.Step> get steps => $_getList(11); - - @$pb.TagNumber(18) - BuildInfra get infra => $_getN(12); - @$pb.TagNumber(18) - set infra(BuildInfra v) { - setField(18, v); - } - - @$pb.TagNumber(18) - $core.bool hasInfra() => $_has(12); - @$pb.TagNumber(18) - void clearInfra() => clearField(18); - @$pb.TagNumber(18) - BuildInfra ensureInfra() => $_ensure(12); - - @$pb.TagNumber(19) - $core.List<$3.StringPair> get tags => $_getList(13); - - @$pb.TagNumber(20) - $core.String get summaryMarkdown => $_getSZ(14); - @$pb.TagNumber(20) - set summaryMarkdown($core.String v) { - $_setString(14, v); - } - - @$pb.TagNumber(20) - $core.bool hasSummaryMarkdown() => $_has(14); - @$pb.TagNumber(20) - void clearSummaryMarkdown() => clearField(20); - - @$pb.TagNumber(21) - $3.Trinary get critical => $_getN(15); - @$pb.TagNumber(21) - set critical($3.Trinary v) { - setField(21, v); - } - - @$pb.TagNumber(21) - $core.bool hasCritical() => $_has(15); - @$pb.TagNumber(21) - void clearCritical() => clearField(21); - - @$pb.TagNumber(22) - $3.StatusDetails get statusDetails => $_getN(16); - @$pb.TagNumber(22) - set statusDetails($3.StatusDetails v) { - setField(22, v); - } - - @$pb.TagNumber(22) - $core.bool hasStatusDetails() => $_has(16); - @$pb.TagNumber(22) - void clearStatusDetails() => clearField(22); - @$pb.TagNumber(22) - $3.StatusDetails ensureStatusDetails() => $_ensure(16); - - @$pb.TagNumber(23) - $core.String get canceledBy => $_getSZ(17); - @$pb.TagNumber(23) - set canceledBy($core.String v) { - $_setString(17, v); - } - - @$pb.TagNumber(23) - $core.bool hasCanceledBy() => $_has(17); - @$pb.TagNumber(23) - void clearCanceledBy() => clearField(23); - - @$pb.TagNumber(24) - $3.Executable get exe => $_getN(18); - @$pb.TagNumber(24) - set exe($3.Executable v) { - setField(24, v); - } - - @$pb.TagNumber(24) - $core.bool hasExe() => $_has(18); - @$pb.TagNumber(24) - void clearExe() => clearField(24); - @$pb.TagNumber(24) - $3.Executable ensureExe() => $_ensure(18); - - @$pb.TagNumber(25) - $core.bool get canary => $_getBF(19); - @$pb.TagNumber(25) - set canary($core.bool v) { - $_setBool(19, v); - } - - @$pb.TagNumber(25) - $core.bool hasCanary() => $_has(19); - @$pb.TagNumber(25) - void clearCanary() => clearField(25); - - @$pb.TagNumber(26) - $4.Duration get schedulingTimeout => $_getN(20); - @$pb.TagNumber(26) - set schedulingTimeout($4.Duration v) { - setField(26, v); - } - - @$pb.TagNumber(26) - $core.bool hasSchedulingTimeout() => $_has(20); - @$pb.TagNumber(26) - void clearSchedulingTimeout() => clearField(26); - @$pb.TagNumber(26) - $4.Duration ensureSchedulingTimeout() => $_ensure(20); - - @$pb.TagNumber(27) - $4.Duration get executionTimeout => $_getN(21); - @$pb.TagNumber(27) - set executionTimeout($4.Duration v) { - setField(27, v); - } - - @$pb.TagNumber(27) - $core.bool hasExecutionTimeout() => $_has(21); - @$pb.TagNumber(27) - void clearExecutionTimeout() => clearField(27); - @$pb.TagNumber(27) - $4.Duration ensureExecutionTimeout() => $_ensure(21); - - @$pb.TagNumber(28) - $core.bool get waitForCapacity => $_getBF(22); - @$pb.TagNumber(28) - set waitForCapacity($core.bool v) { - $_setBool(22, v); - } - - @$pb.TagNumber(28) - $core.bool hasWaitForCapacity() => $_has(22); - @$pb.TagNumber(28) - void clearWaitForCapacity() => clearField(28); - - @$pb.TagNumber(29) - $4.Duration get gracePeriod => $_getN(23); - @$pb.TagNumber(29) - set gracePeriod($4.Duration v) { - setField(29, v); - } - - @$pb.TagNumber(29) - $core.bool hasGracePeriod() => $_has(23); - @$pb.TagNumber(29) - void clearGracePeriod() => clearField(29); - @$pb.TagNumber(29) - $4.Duration ensureGracePeriod() => $_ensure(23); - - @$pb.TagNumber(30) - $core.bool get canOutliveParent => $_getBF(24); - @$pb.TagNumber(30) - set canOutliveParent($core.bool v) { - $_setBool(24, v); - } - - @$pb.TagNumber(30) - $core.bool hasCanOutliveParent() => $_has(24); - @$pb.TagNumber(30) - void clearCanOutliveParent() => clearField(30); - - @$pb.TagNumber(31) - $core.List<$fixnum.Int64> get ancestorIds => $_getList(25); - - @$pb.TagNumber(32) - $1.Timestamp get cancelTime => $_getN(26); - @$pb.TagNumber(32) - set cancelTime($1.Timestamp v) { - setField(32, v); - } - - @$pb.TagNumber(32) - $core.bool hasCancelTime() => $_has(26); - @$pb.TagNumber(32) - void clearCancelTime() => clearField(32); - @$pb.TagNumber(32) - $1.Timestamp ensureCancelTime() => $_ensure(26); - - @$pb.TagNumber(33) - $core.String get cancellationMarkdown => $_getSZ(27); - @$pb.TagNumber(33) - set cancellationMarkdown($core.String v) { - $_setString(27, v); - } - - @$pb.TagNumber(33) - $core.bool hasCancellationMarkdown() => $_has(27); - @$pb.TagNumber(33) - void clearCancellationMarkdown() => clearField(33); - - @$pb.TagNumber(34) - Build_BuilderInfo get builderInfo => $_getN(28); - @$pb.TagNumber(34) - set builderInfo(Build_BuilderInfo v) { - setField(34, v); - } - - @$pb.TagNumber(34) - $core.bool hasBuilderInfo() => $_has(28); - @$pb.TagNumber(34) - void clearBuilderInfo() => clearField(34); - @$pb.TagNumber(34) - Build_BuilderInfo ensureBuilderInfo() => $_ensure(28); - - @$pb.TagNumber(35) - $3.Trinary get retriable => $_getN(29); - @$pb.TagNumber(35) - set retriable($3.Trinary v) { - setField(35, v); - } - - @$pb.TagNumber(35) - $core.bool hasRetriable() => $_has(29); - @$pb.TagNumber(35) - void clearRetriable() => clearField(35); -} - -class InputDataRef_CAS_Digest extends $pb.GeneratedMessage { - factory InputDataRef_CAS_Digest() => create(); - InputDataRef_CAS_Digest._() : super(); - factory InputDataRef_CAS_Digest.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory InputDataRef_CAS_Digest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'InputDataRef.CAS.Digest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'hash') - ..aInt64(2, _omitFieldNames ? '' : 'sizeBytes') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - InputDataRef_CAS_Digest clone() => InputDataRef_CAS_Digest()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - InputDataRef_CAS_Digest copyWith(void Function(InputDataRef_CAS_Digest) updates) => - super.copyWith((message) => updates(message as InputDataRef_CAS_Digest)) as InputDataRef_CAS_Digest; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static InputDataRef_CAS_Digest create() => InputDataRef_CAS_Digest._(); - InputDataRef_CAS_Digest createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static InputDataRef_CAS_Digest getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static InputDataRef_CAS_Digest? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get hash => $_getSZ(0); - @$pb.TagNumber(1) - set hash($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasHash() => $_has(0); - @$pb.TagNumber(1) - void clearHash() => clearField(1); - - @$pb.TagNumber(2) - $fixnum.Int64 get sizeBytes => $_getI64(1); - @$pb.TagNumber(2) - set sizeBytes($fixnum.Int64 v) { - $_setInt64(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasSizeBytes() => $_has(1); - @$pb.TagNumber(2) - void clearSizeBytes() => clearField(2); -} - -class InputDataRef_CAS extends $pb.GeneratedMessage { - factory InputDataRef_CAS() => create(); - InputDataRef_CAS._() : super(); - factory InputDataRef_CAS.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory InputDataRef_CAS.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'InputDataRef.CAS', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'casInstance') - ..aOM(2, _omitFieldNames ? '' : 'digest', subBuilder: InputDataRef_CAS_Digest.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - InputDataRef_CAS clone() => InputDataRef_CAS()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - InputDataRef_CAS copyWith(void Function(InputDataRef_CAS) updates) => - super.copyWith((message) => updates(message as InputDataRef_CAS)) as InputDataRef_CAS; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static InputDataRef_CAS create() => InputDataRef_CAS._(); - InputDataRef_CAS createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static InputDataRef_CAS getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static InputDataRef_CAS? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get casInstance => $_getSZ(0); - @$pb.TagNumber(1) - set casInstance($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasCasInstance() => $_has(0); - @$pb.TagNumber(1) - void clearCasInstance() => clearField(1); - - @$pb.TagNumber(2) - InputDataRef_CAS_Digest get digest => $_getN(1); - @$pb.TagNumber(2) - set digest(InputDataRef_CAS_Digest v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasDigest() => $_has(1); - @$pb.TagNumber(2) - void clearDigest() => clearField(2); - @$pb.TagNumber(2) - InputDataRef_CAS_Digest ensureDigest() => $_ensure(1); -} - -class InputDataRef_CIPD_PkgSpec extends $pb.GeneratedMessage { - factory InputDataRef_CIPD_PkgSpec() => create(); - InputDataRef_CIPD_PkgSpec._() : super(); - factory InputDataRef_CIPD_PkgSpec.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory InputDataRef_CIPD_PkgSpec.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'InputDataRef.CIPD.PkgSpec', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'package') - ..aOS(2, _omitFieldNames ? '' : 'version') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - InputDataRef_CIPD_PkgSpec clone() => InputDataRef_CIPD_PkgSpec()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - InputDataRef_CIPD_PkgSpec copyWith(void Function(InputDataRef_CIPD_PkgSpec) updates) => - super.copyWith((message) => updates(message as InputDataRef_CIPD_PkgSpec)) as InputDataRef_CIPD_PkgSpec; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static InputDataRef_CIPD_PkgSpec create() => InputDataRef_CIPD_PkgSpec._(); - InputDataRef_CIPD_PkgSpec createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static InputDataRef_CIPD_PkgSpec getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static InputDataRef_CIPD_PkgSpec? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get package => $_getSZ(0); - @$pb.TagNumber(1) - set package($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasPackage() => $_has(0); - @$pb.TagNumber(1) - void clearPackage() => clearField(1); - - @$pb.TagNumber(2) - $core.String get version => $_getSZ(1); - @$pb.TagNumber(2) - set version($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasVersion() => $_has(1); - @$pb.TagNumber(2) - void clearVersion() => clearField(2); -} - -class InputDataRef_CIPD extends $pb.GeneratedMessage { - factory InputDataRef_CIPD() => create(); - InputDataRef_CIPD._() : super(); - factory InputDataRef_CIPD.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory InputDataRef_CIPD.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'InputDataRef.CIPD', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'server') - ..pc(2, _omitFieldNames ? '' : 'specs', $pb.PbFieldType.PM, - subBuilder: InputDataRef_CIPD_PkgSpec.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - InputDataRef_CIPD clone() => InputDataRef_CIPD()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - InputDataRef_CIPD copyWith(void Function(InputDataRef_CIPD) updates) => - super.copyWith((message) => updates(message as InputDataRef_CIPD)) as InputDataRef_CIPD; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static InputDataRef_CIPD create() => InputDataRef_CIPD._(); - InputDataRef_CIPD createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static InputDataRef_CIPD getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static InputDataRef_CIPD? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get server => $_getSZ(0); - @$pb.TagNumber(1) - set server($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasServer() => $_has(0); - @$pb.TagNumber(1) - void clearServer() => clearField(1); - - @$pb.TagNumber(2) - $core.List get specs => $_getList(1); -} - -enum InputDataRef_DataType { cas, cipd, notSet } - -class InputDataRef extends $pb.GeneratedMessage { - factory InputDataRef() => create(); - InputDataRef._() : super(); - factory InputDataRef.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory InputDataRef.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static const $core.Map<$core.int, InputDataRef_DataType> _InputDataRef_DataTypeByTag = { - 1: InputDataRef_DataType.cas, - 2: InputDataRef_DataType.cipd, - 0: InputDataRef_DataType.notSet - }; - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'InputDataRef', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..oo(0, [1, 2]) - ..aOM(1, _omitFieldNames ? '' : 'cas', subBuilder: InputDataRef_CAS.create) - ..aOM(2, _omitFieldNames ? '' : 'cipd', subBuilder: InputDataRef_CIPD.create) - ..pPS(3, _omitFieldNames ? '' : 'onPath') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - InputDataRef clone() => InputDataRef()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - InputDataRef copyWith(void Function(InputDataRef) updates) => - super.copyWith((message) => updates(message as InputDataRef)) as InputDataRef; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static InputDataRef create() => InputDataRef._(); - InputDataRef createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static InputDataRef getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static InputDataRef? _defaultInstance; - - InputDataRef_DataType whichDataType() => _InputDataRef_DataTypeByTag[$_whichOneof(0)]!; - void clearDataType() => clearField($_whichOneof(0)); - - @$pb.TagNumber(1) - InputDataRef_CAS get cas => $_getN(0); - @$pb.TagNumber(1) - set cas(InputDataRef_CAS v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasCas() => $_has(0); - @$pb.TagNumber(1) - void clearCas() => clearField(1); - @$pb.TagNumber(1) - InputDataRef_CAS ensureCas() => $_ensure(0); - - @$pb.TagNumber(2) - InputDataRef_CIPD get cipd => $_getN(1); - @$pb.TagNumber(2) - set cipd(InputDataRef_CIPD v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasCipd() => $_has(1); - @$pb.TagNumber(2) - void clearCipd() => clearField(2); - @$pb.TagNumber(2) - InputDataRef_CIPD ensureCipd() => $_ensure(1); - - @$pb.TagNumber(3) - $core.List<$core.String> get onPath => $_getList(2); -} - -class ResolvedDataRef_Timing extends $pb.GeneratedMessage { - factory ResolvedDataRef_Timing() => create(); - ResolvedDataRef_Timing._() : super(); - factory ResolvedDataRef_Timing.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory ResolvedDataRef_Timing.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ResolvedDataRef.Timing', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOM<$4.Duration>(1, _omitFieldNames ? '' : 'fetchDuration', subBuilder: $4.Duration.create) - ..aOM<$4.Duration>(2, _omitFieldNames ? '' : 'installDuration', subBuilder: $4.Duration.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - ResolvedDataRef_Timing clone() => ResolvedDataRef_Timing()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - ResolvedDataRef_Timing copyWith(void Function(ResolvedDataRef_Timing) updates) => - super.copyWith((message) => updates(message as ResolvedDataRef_Timing)) as ResolvedDataRef_Timing; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static ResolvedDataRef_Timing create() => ResolvedDataRef_Timing._(); - ResolvedDataRef_Timing createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static ResolvedDataRef_Timing getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static ResolvedDataRef_Timing? _defaultInstance; - - @$pb.TagNumber(1) - $4.Duration get fetchDuration => $_getN(0); - @$pb.TagNumber(1) - set fetchDuration($4.Duration v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasFetchDuration() => $_has(0); - @$pb.TagNumber(1) - void clearFetchDuration() => clearField(1); - @$pb.TagNumber(1) - $4.Duration ensureFetchDuration() => $_ensure(0); - - @$pb.TagNumber(2) - $4.Duration get installDuration => $_getN(1); - @$pb.TagNumber(2) - set installDuration($4.Duration v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasInstallDuration() => $_has(1); - @$pb.TagNumber(2) - void clearInstallDuration() => clearField(2); - @$pb.TagNumber(2) - $4.Duration ensureInstallDuration() => $_ensure(1); -} - -class ResolvedDataRef_CAS extends $pb.GeneratedMessage { - factory ResolvedDataRef_CAS() => create(); - ResolvedDataRef_CAS._() : super(); - factory ResolvedDataRef_CAS.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory ResolvedDataRef_CAS.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ResolvedDataRef.CAS', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOM(1, _omitFieldNames ? '' : 'timing', subBuilder: ResolvedDataRef_Timing.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - ResolvedDataRef_CAS clone() => ResolvedDataRef_CAS()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - ResolvedDataRef_CAS copyWith(void Function(ResolvedDataRef_CAS) updates) => - super.copyWith((message) => updates(message as ResolvedDataRef_CAS)) as ResolvedDataRef_CAS; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static ResolvedDataRef_CAS create() => ResolvedDataRef_CAS._(); - ResolvedDataRef_CAS createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static ResolvedDataRef_CAS getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static ResolvedDataRef_CAS? _defaultInstance; - - @$pb.TagNumber(1) - ResolvedDataRef_Timing get timing => $_getN(0); - @$pb.TagNumber(1) - set timing(ResolvedDataRef_Timing v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasTiming() => $_has(0); - @$pb.TagNumber(1) - void clearTiming() => clearField(1); - @$pb.TagNumber(1) - ResolvedDataRef_Timing ensureTiming() => $_ensure(0); -} - -class ResolvedDataRef_CIPD_PkgSpec extends $pb.GeneratedMessage { - factory ResolvedDataRef_CIPD_PkgSpec() => create(); - ResolvedDataRef_CIPD_PkgSpec._() : super(); - factory ResolvedDataRef_CIPD_PkgSpec.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory ResolvedDataRef_CIPD_PkgSpec.fromJson($core.String i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ResolvedDataRef.CIPD.PkgSpec', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOB(1, _omitFieldNames ? '' : 'skipped') - ..aOS(2, _omitFieldNames ? '' : 'package') - ..aOS(3, _omitFieldNames ? '' : 'version') - ..e<$3.Trinary>(4, _omitFieldNames ? '' : 'wasCached', $pb.PbFieldType.OE, - defaultOrMaker: $3.Trinary.UNSET, valueOf: $3.Trinary.valueOf, enumValues: $3.Trinary.values) - ..aOM(5, _omitFieldNames ? '' : 'timing', subBuilder: ResolvedDataRef_Timing.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - ResolvedDataRef_CIPD_PkgSpec clone() => ResolvedDataRef_CIPD_PkgSpec()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - ResolvedDataRef_CIPD_PkgSpec copyWith(void Function(ResolvedDataRef_CIPD_PkgSpec) updates) => - super.copyWith((message) => updates(message as ResolvedDataRef_CIPD_PkgSpec)) as ResolvedDataRef_CIPD_PkgSpec; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static ResolvedDataRef_CIPD_PkgSpec create() => ResolvedDataRef_CIPD_PkgSpec._(); - ResolvedDataRef_CIPD_PkgSpec createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static ResolvedDataRef_CIPD_PkgSpec getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static ResolvedDataRef_CIPD_PkgSpec? _defaultInstance; - - @$pb.TagNumber(1) - $core.bool get skipped => $_getBF(0); - @$pb.TagNumber(1) - set skipped($core.bool v) { - $_setBool(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasSkipped() => $_has(0); - @$pb.TagNumber(1) - void clearSkipped() => clearField(1); - - @$pb.TagNumber(2) - $core.String get package => $_getSZ(1); - @$pb.TagNumber(2) - set package($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasPackage() => $_has(1); - @$pb.TagNumber(2) - void clearPackage() => clearField(2); - - @$pb.TagNumber(3) - $core.String get version => $_getSZ(2); - @$pb.TagNumber(3) - set version($core.String v) { - $_setString(2, v); - } - - @$pb.TagNumber(3) - $core.bool hasVersion() => $_has(2); - @$pb.TagNumber(3) - void clearVersion() => clearField(3); - - @$pb.TagNumber(4) - $3.Trinary get wasCached => $_getN(3); - @$pb.TagNumber(4) - set wasCached($3.Trinary v) { - setField(4, v); - } - - @$pb.TagNumber(4) - $core.bool hasWasCached() => $_has(3); - @$pb.TagNumber(4) - void clearWasCached() => clearField(4); - - @$pb.TagNumber(5) - ResolvedDataRef_Timing get timing => $_getN(4); - @$pb.TagNumber(5) - set timing(ResolvedDataRef_Timing v) { - setField(5, v); - } - - @$pb.TagNumber(5) - $core.bool hasTiming() => $_has(4); - @$pb.TagNumber(5) - void clearTiming() => clearField(5); - @$pb.TagNumber(5) - ResolvedDataRef_Timing ensureTiming() => $_ensure(4); -} - -class ResolvedDataRef_CIPD extends $pb.GeneratedMessage { - factory ResolvedDataRef_CIPD() => create(); - ResolvedDataRef_CIPD._() : super(); - factory ResolvedDataRef_CIPD.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory ResolvedDataRef_CIPD.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ResolvedDataRef.CIPD', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..pc(2, _omitFieldNames ? '' : 'specs', $pb.PbFieldType.PM, - subBuilder: ResolvedDataRef_CIPD_PkgSpec.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - ResolvedDataRef_CIPD clone() => ResolvedDataRef_CIPD()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - ResolvedDataRef_CIPD copyWith(void Function(ResolvedDataRef_CIPD) updates) => - super.copyWith((message) => updates(message as ResolvedDataRef_CIPD)) as ResolvedDataRef_CIPD; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static ResolvedDataRef_CIPD create() => ResolvedDataRef_CIPD._(); - ResolvedDataRef_CIPD createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static ResolvedDataRef_CIPD getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static ResolvedDataRef_CIPD? _defaultInstance; - - @$pb.TagNumber(2) - $core.List get specs => $_getList(0); -} - -enum ResolvedDataRef_DataType { cas, cipd, notSet } - -class ResolvedDataRef extends $pb.GeneratedMessage { - factory ResolvedDataRef() => create(); - ResolvedDataRef._() : super(); - factory ResolvedDataRef.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory ResolvedDataRef.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static const $core.Map<$core.int, ResolvedDataRef_DataType> _ResolvedDataRef_DataTypeByTag = { - 1: ResolvedDataRef_DataType.cas, - 2: ResolvedDataRef_DataType.cipd, - 0: ResolvedDataRef_DataType.notSet - }; - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ResolvedDataRef', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..oo(0, [1, 2]) - ..aOM(1, _omitFieldNames ? '' : 'cas', subBuilder: ResolvedDataRef_CAS.create) - ..aOM(2, _omitFieldNames ? '' : 'cipd', subBuilder: ResolvedDataRef_CIPD.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - ResolvedDataRef clone() => ResolvedDataRef()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - ResolvedDataRef copyWith(void Function(ResolvedDataRef) updates) => - super.copyWith((message) => updates(message as ResolvedDataRef)) as ResolvedDataRef; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static ResolvedDataRef create() => ResolvedDataRef._(); - ResolvedDataRef createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static ResolvedDataRef getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static ResolvedDataRef? _defaultInstance; - - ResolvedDataRef_DataType whichDataType() => _ResolvedDataRef_DataTypeByTag[$_whichOneof(0)]!; - void clearDataType() => clearField($_whichOneof(0)); - - @$pb.TagNumber(1) - ResolvedDataRef_CAS get cas => $_getN(0); - @$pb.TagNumber(1) - set cas(ResolvedDataRef_CAS v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasCas() => $_has(0); - @$pb.TagNumber(1) - void clearCas() => clearField(1); - @$pb.TagNumber(1) - ResolvedDataRef_CAS ensureCas() => $_ensure(0); - - @$pb.TagNumber(2) - ResolvedDataRef_CIPD get cipd => $_getN(1); - @$pb.TagNumber(2) - set cipd(ResolvedDataRef_CIPD v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasCipd() => $_has(1); - @$pb.TagNumber(2) - void clearCipd() => clearField(2); - @$pb.TagNumber(2) - ResolvedDataRef_CIPD ensureCipd() => $_ensure(1); -} - -class BuildInfra_Buildbucket_Agent_Source_CIPD extends $pb.GeneratedMessage { - factory BuildInfra_Buildbucket_Agent_Source_CIPD() => create(); - BuildInfra_Buildbucket_Agent_Source_CIPD._() : super(); - factory BuildInfra_Buildbucket_Agent_Source_CIPD.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuildInfra_Buildbucket_Agent_Source_CIPD.fromJson($core.String i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuildInfra.Buildbucket.Agent.Source.CIPD', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'package') - ..aOS(2, _omitFieldNames ? '' : 'version') - ..aOS(3, _omitFieldNames ? '' : 'server') - ..m<$core.String, $core.String>(4, _omitFieldNames ? '' : 'resolvedInstances', - entryClassName: 'BuildInfra.Buildbucket.Agent.Source.CIPD.ResolvedInstancesEntry', - keyFieldType: $pb.PbFieldType.OS, - valueFieldType: $pb.PbFieldType.OS, - packageName: const $pb.PackageName('buildbucket.v2')) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuildInfra_Buildbucket_Agent_Source_CIPD clone() => - BuildInfra_Buildbucket_Agent_Source_CIPD()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuildInfra_Buildbucket_Agent_Source_CIPD copyWith(void Function(BuildInfra_Buildbucket_Agent_Source_CIPD) updates) => - super.copyWith((message) => updates(message as BuildInfra_Buildbucket_Agent_Source_CIPD)) - as BuildInfra_Buildbucket_Agent_Source_CIPD; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuildInfra_Buildbucket_Agent_Source_CIPD create() => BuildInfra_Buildbucket_Agent_Source_CIPD._(); - BuildInfra_Buildbucket_Agent_Source_CIPD createEmptyInstance() => create(); - static $pb.PbList createRepeated() => - $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuildInfra_Buildbucket_Agent_Source_CIPD getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuildInfra_Buildbucket_Agent_Source_CIPD? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get package => $_getSZ(0); - @$pb.TagNumber(1) - set package($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasPackage() => $_has(0); - @$pb.TagNumber(1) - void clearPackage() => clearField(1); - - @$pb.TagNumber(2) - $core.String get version => $_getSZ(1); - @$pb.TagNumber(2) - set version($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasVersion() => $_has(1); - @$pb.TagNumber(2) - void clearVersion() => clearField(2); - - @$pb.TagNumber(3) - $core.String get server => $_getSZ(2); - @$pb.TagNumber(3) - set server($core.String v) { - $_setString(2, v); - } - - @$pb.TagNumber(3) - $core.bool hasServer() => $_has(2); - @$pb.TagNumber(3) - void clearServer() => clearField(3); - - @$pb.TagNumber(4) - $core.Map<$core.String, $core.String> get resolvedInstances => $_getMap(3); -} - -enum BuildInfra_Buildbucket_Agent_Source_DataType { cipd, notSet } - -class BuildInfra_Buildbucket_Agent_Source extends $pb.GeneratedMessage { - factory BuildInfra_Buildbucket_Agent_Source() => create(); - BuildInfra_Buildbucket_Agent_Source._() : super(); - factory BuildInfra_Buildbucket_Agent_Source.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuildInfra_Buildbucket_Agent_Source.fromJson($core.String i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static const $core.Map<$core.int, BuildInfra_Buildbucket_Agent_Source_DataType> - _BuildInfra_Buildbucket_Agent_Source_DataTypeByTag = { - 1: BuildInfra_Buildbucket_Agent_Source_DataType.cipd, - 0: BuildInfra_Buildbucket_Agent_Source_DataType.notSet - }; - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuildInfra.Buildbucket.Agent.Source', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..oo(0, [1]) - ..aOM(1, _omitFieldNames ? '' : 'cipd', - subBuilder: BuildInfra_Buildbucket_Agent_Source_CIPD.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuildInfra_Buildbucket_Agent_Source clone() => BuildInfra_Buildbucket_Agent_Source()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuildInfra_Buildbucket_Agent_Source copyWith(void Function(BuildInfra_Buildbucket_Agent_Source) updates) => - super.copyWith((message) => updates(message as BuildInfra_Buildbucket_Agent_Source)) - as BuildInfra_Buildbucket_Agent_Source; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuildInfra_Buildbucket_Agent_Source create() => BuildInfra_Buildbucket_Agent_Source._(); - BuildInfra_Buildbucket_Agent_Source createEmptyInstance() => create(); - static $pb.PbList createRepeated() => - $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuildInfra_Buildbucket_Agent_Source getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuildInfra_Buildbucket_Agent_Source? _defaultInstance; - - BuildInfra_Buildbucket_Agent_Source_DataType whichDataType() => - _BuildInfra_Buildbucket_Agent_Source_DataTypeByTag[$_whichOneof(0)]!; - void clearDataType() => clearField($_whichOneof(0)); - - @$pb.TagNumber(1) - BuildInfra_Buildbucket_Agent_Source_CIPD get cipd => $_getN(0); - @$pb.TagNumber(1) - set cipd(BuildInfra_Buildbucket_Agent_Source_CIPD v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasCipd() => $_has(0); - @$pb.TagNumber(1) - void clearCipd() => clearField(1); - @$pb.TagNumber(1) - BuildInfra_Buildbucket_Agent_Source_CIPD ensureCipd() => $_ensure(0); -} - -class BuildInfra_Buildbucket_Agent_Input extends $pb.GeneratedMessage { - factory BuildInfra_Buildbucket_Agent_Input() => create(); - BuildInfra_Buildbucket_Agent_Input._() : super(); - factory BuildInfra_Buildbucket_Agent_Input.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuildInfra_Buildbucket_Agent_Input.fromJson($core.String i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuildInfra.Buildbucket.Agent.Input', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..m<$core.String, InputDataRef>(1, _omitFieldNames ? '' : 'data', - entryClassName: 'BuildInfra.Buildbucket.Agent.Input.DataEntry', - keyFieldType: $pb.PbFieldType.OS, - valueFieldType: $pb.PbFieldType.OM, - valueCreator: InputDataRef.create, - valueDefaultOrMaker: InputDataRef.getDefault, - packageName: const $pb.PackageName('buildbucket.v2')) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuildInfra_Buildbucket_Agent_Input clone() => BuildInfra_Buildbucket_Agent_Input()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuildInfra_Buildbucket_Agent_Input copyWith(void Function(BuildInfra_Buildbucket_Agent_Input) updates) => - super.copyWith((message) => updates(message as BuildInfra_Buildbucket_Agent_Input)) - as BuildInfra_Buildbucket_Agent_Input; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuildInfra_Buildbucket_Agent_Input create() => BuildInfra_Buildbucket_Agent_Input._(); - BuildInfra_Buildbucket_Agent_Input createEmptyInstance() => create(); - static $pb.PbList createRepeated() => - $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuildInfra_Buildbucket_Agent_Input getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuildInfra_Buildbucket_Agent_Input? _defaultInstance; - - @$pb.TagNumber(1) - $core.Map<$core.String, InputDataRef> get data => $_getMap(0); -} - -class BuildInfra_Buildbucket_Agent_Output extends $pb.GeneratedMessage { - factory BuildInfra_Buildbucket_Agent_Output() => create(); - BuildInfra_Buildbucket_Agent_Output._() : super(); - factory BuildInfra_Buildbucket_Agent_Output.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuildInfra_Buildbucket_Agent_Output.fromJson($core.String i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuildInfra.Buildbucket.Agent.Output', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..m<$core.String, ResolvedDataRef>(1, _omitFieldNames ? '' : 'resolvedData', - entryClassName: 'BuildInfra.Buildbucket.Agent.Output.ResolvedDataEntry', - keyFieldType: $pb.PbFieldType.OS, - valueFieldType: $pb.PbFieldType.OM, - valueCreator: ResolvedDataRef.create, - valueDefaultOrMaker: ResolvedDataRef.getDefault, - packageName: const $pb.PackageName('buildbucket.v2')) - ..e<$3.Status>(2, _omitFieldNames ? '' : 'status', $pb.PbFieldType.OE, - defaultOrMaker: $3.Status.STATUS_UNSPECIFIED, valueOf: $3.Status.valueOf, enumValues: $3.Status.values) - ..aOM<$3.StatusDetails>(3, _omitFieldNames ? '' : 'statusDetails', subBuilder: $3.StatusDetails.create) - ..aOS(4, _omitFieldNames ? '' : 'summaryHtml') - ..aOS(5, _omitFieldNames ? '' : 'agentPlatform') - ..aOM<$4.Duration>(6, _omitFieldNames ? '' : 'totalDuration', subBuilder: $4.Duration.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuildInfra_Buildbucket_Agent_Output clone() => BuildInfra_Buildbucket_Agent_Output()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuildInfra_Buildbucket_Agent_Output copyWith(void Function(BuildInfra_Buildbucket_Agent_Output) updates) => - super.copyWith((message) => updates(message as BuildInfra_Buildbucket_Agent_Output)) - as BuildInfra_Buildbucket_Agent_Output; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuildInfra_Buildbucket_Agent_Output create() => BuildInfra_Buildbucket_Agent_Output._(); - BuildInfra_Buildbucket_Agent_Output createEmptyInstance() => create(); - static $pb.PbList createRepeated() => - $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuildInfra_Buildbucket_Agent_Output getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuildInfra_Buildbucket_Agent_Output? _defaultInstance; - - @$pb.TagNumber(1) - $core.Map<$core.String, ResolvedDataRef> get resolvedData => $_getMap(0); - - @$pb.TagNumber(2) - $3.Status get status => $_getN(1); - @$pb.TagNumber(2) - set status($3.Status v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasStatus() => $_has(1); - @$pb.TagNumber(2) - void clearStatus() => clearField(2); - - @$pb.TagNumber(3) - $3.StatusDetails get statusDetails => $_getN(2); - @$pb.TagNumber(3) - set statusDetails($3.StatusDetails v) { - setField(3, v); - } - - @$pb.TagNumber(3) - $core.bool hasStatusDetails() => $_has(2); - @$pb.TagNumber(3) - void clearStatusDetails() => clearField(3); - @$pb.TagNumber(3) - $3.StatusDetails ensureStatusDetails() => $_ensure(2); - - @$pb.TagNumber(4) - $core.String get summaryHtml => $_getSZ(3); - @$pb.TagNumber(4) - set summaryHtml($core.String v) { - $_setString(3, v); - } - - @$pb.TagNumber(4) - $core.bool hasSummaryHtml() => $_has(3); - @$pb.TagNumber(4) - void clearSummaryHtml() => clearField(4); - - @$pb.TagNumber(5) - $core.String get agentPlatform => $_getSZ(4); - @$pb.TagNumber(5) - set agentPlatform($core.String v) { - $_setString(4, v); - } - - @$pb.TagNumber(5) - $core.bool hasAgentPlatform() => $_has(4); - @$pb.TagNumber(5) - void clearAgentPlatform() => clearField(5); - - @$pb.TagNumber(6) - $4.Duration get totalDuration => $_getN(5); - @$pb.TagNumber(6) - set totalDuration($4.Duration v) { - setField(6, v); - } - - @$pb.TagNumber(6) - $core.bool hasTotalDuration() => $_has(5); - @$pb.TagNumber(6) - void clearTotalDuration() => clearField(6); - @$pb.TagNumber(6) - $4.Duration ensureTotalDuration() => $_ensure(5); -} - -class BuildInfra_Buildbucket_Agent extends $pb.GeneratedMessage { - factory BuildInfra_Buildbucket_Agent() => create(); - BuildInfra_Buildbucket_Agent._() : super(); - factory BuildInfra_Buildbucket_Agent.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuildInfra_Buildbucket_Agent.fromJson($core.String i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuildInfra.Buildbucket.Agent', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOM(1, _omitFieldNames ? '' : 'input', - subBuilder: BuildInfra_Buildbucket_Agent_Input.create) - ..aOM(2, _omitFieldNames ? '' : 'output', - subBuilder: BuildInfra_Buildbucket_Agent_Output.create) - ..aOM(3, _omitFieldNames ? '' : 'source', - subBuilder: BuildInfra_Buildbucket_Agent_Source.create) - ..m<$core.String, BuildInfra_Buildbucket_Agent_Purpose>(4, _omitFieldNames ? '' : 'purposes', - entryClassName: 'BuildInfra.Buildbucket.Agent.PurposesEntry', - keyFieldType: $pb.PbFieldType.OS, - valueFieldType: $pb.PbFieldType.OE, - valueOf: BuildInfra_Buildbucket_Agent_Purpose.valueOf, - enumValues: BuildInfra_Buildbucket_Agent_Purpose.values, - valueDefaultOrMaker: BuildInfra_Buildbucket_Agent_Purpose.PURPOSE_UNSPECIFIED, - defaultEnumValue: BuildInfra_Buildbucket_Agent_Purpose.PURPOSE_UNSPECIFIED, - packageName: const $pb.PackageName('buildbucket.v2')) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuildInfra_Buildbucket_Agent clone() => BuildInfra_Buildbucket_Agent()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuildInfra_Buildbucket_Agent copyWith(void Function(BuildInfra_Buildbucket_Agent) updates) => - super.copyWith((message) => updates(message as BuildInfra_Buildbucket_Agent)) as BuildInfra_Buildbucket_Agent; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuildInfra_Buildbucket_Agent create() => BuildInfra_Buildbucket_Agent._(); - BuildInfra_Buildbucket_Agent createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuildInfra_Buildbucket_Agent getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuildInfra_Buildbucket_Agent? _defaultInstance; - - @$pb.TagNumber(1) - BuildInfra_Buildbucket_Agent_Input get input => $_getN(0); - @$pb.TagNumber(1) - set input(BuildInfra_Buildbucket_Agent_Input v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasInput() => $_has(0); - @$pb.TagNumber(1) - void clearInput() => clearField(1); - @$pb.TagNumber(1) - BuildInfra_Buildbucket_Agent_Input ensureInput() => $_ensure(0); - - @$pb.TagNumber(2) - BuildInfra_Buildbucket_Agent_Output get output => $_getN(1); - @$pb.TagNumber(2) - set output(BuildInfra_Buildbucket_Agent_Output v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasOutput() => $_has(1); - @$pb.TagNumber(2) - void clearOutput() => clearField(2); - @$pb.TagNumber(2) - BuildInfra_Buildbucket_Agent_Output ensureOutput() => $_ensure(1); - - @$pb.TagNumber(3) - BuildInfra_Buildbucket_Agent_Source get source => $_getN(2); - @$pb.TagNumber(3) - set source(BuildInfra_Buildbucket_Agent_Source v) { - setField(3, v); - } - - @$pb.TagNumber(3) - $core.bool hasSource() => $_has(2); - @$pb.TagNumber(3) - void clearSource() => clearField(3); - @$pb.TagNumber(3) - BuildInfra_Buildbucket_Agent_Source ensureSource() => $_ensure(2); - - @$pb.TagNumber(4) - $core.Map<$core.String, BuildInfra_Buildbucket_Agent_Purpose> get purposes => $_getMap(3); -} - -class BuildInfra_Buildbucket extends $pb.GeneratedMessage { - factory BuildInfra_Buildbucket() => create(); - BuildInfra_Buildbucket._() : super(); - factory BuildInfra_Buildbucket.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuildInfra_Buildbucket.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuildInfra.Buildbucket', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(2, _omitFieldNames ? '' : 'serviceConfigRevision') - ..aOM<$5.Struct>(5, _omitFieldNames ? '' : 'requestedProperties', subBuilder: $5.Struct.create) - ..pc<$3.RequestedDimension>(6, _omitFieldNames ? '' : 'requestedDimensions', $pb.PbFieldType.PM, - subBuilder: $3.RequestedDimension.create) - ..aOS(7, _omitFieldNames ? '' : 'hostname') - ..m<$core.String, BuildInfra_Buildbucket_ExperimentReason>(8, _omitFieldNames ? '' : 'experimentReasons', - entryClassName: 'BuildInfra.Buildbucket.ExperimentReasonsEntry', - keyFieldType: $pb.PbFieldType.OS, - valueFieldType: $pb.PbFieldType.OE, - valueOf: BuildInfra_Buildbucket_ExperimentReason.valueOf, - enumValues: BuildInfra_Buildbucket_ExperimentReason.values, - valueDefaultOrMaker: BuildInfra_Buildbucket_ExperimentReason.EXPERIMENT_REASON_UNSET, - defaultEnumValue: BuildInfra_Buildbucket_ExperimentReason.EXPERIMENT_REASON_UNSET, - packageName: const $pb.PackageName('buildbucket.v2')) - ..m<$core.String, ResolvedDataRef>(9, _omitFieldNames ? '' : 'agentExecutable', - entryClassName: 'BuildInfra.Buildbucket.AgentExecutableEntry', - keyFieldType: $pb.PbFieldType.OS, - valueFieldType: $pb.PbFieldType.OM, - valueCreator: ResolvedDataRef.create, - valueDefaultOrMaker: ResolvedDataRef.getDefault, - packageName: const $pb.PackageName('buildbucket.v2')) - ..aOM(10, _omitFieldNames ? '' : 'agent', - subBuilder: BuildInfra_Buildbucket_Agent.create) - ..pPS(11, _omitFieldNames ? '' : 'knownPublicGerritHosts') - ..aOB(12, _omitFieldNames ? '' : 'buildNumber') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuildInfra_Buildbucket clone() => BuildInfra_Buildbucket()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuildInfra_Buildbucket copyWith(void Function(BuildInfra_Buildbucket) updates) => - super.copyWith((message) => updates(message as BuildInfra_Buildbucket)) as BuildInfra_Buildbucket; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuildInfra_Buildbucket create() => BuildInfra_Buildbucket._(); - BuildInfra_Buildbucket createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuildInfra_Buildbucket getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuildInfra_Buildbucket? _defaultInstance; - - @$pb.TagNumber(2) - $core.String get serviceConfigRevision => $_getSZ(0); - @$pb.TagNumber(2) - set serviceConfigRevision($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(2) - $core.bool hasServiceConfigRevision() => $_has(0); - @$pb.TagNumber(2) - void clearServiceConfigRevision() => clearField(2); - - @$pb.TagNumber(5) - $5.Struct get requestedProperties => $_getN(1); - @$pb.TagNumber(5) - set requestedProperties($5.Struct v) { - setField(5, v); - } - - @$pb.TagNumber(5) - $core.bool hasRequestedProperties() => $_has(1); - @$pb.TagNumber(5) - void clearRequestedProperties() => clearField(5); - @$pb.TagNumber(5) - $5.Struct ensureRequestedProperties() => $_ensure(1); - - @$pb.TagNumber(6) - $core.List<$3.RequestedDimension> get requestedDimensions => $_getList(2); - - @$pb.TagNumber(7) - $core.String get hostname => $_getSZ(3); - @$pb.TagNumber(7) - set hostname($core.String v) { - $_setString(3, v); - } - - @$pb.TagNumber(7) - $core.bool hasHostname() => $_has(3); - @$pb.TagNumber(7) - void clearHostname() => clearField(7); - - @$pb.TagNumber(8) - $core.Map<$core.String, BuildInfra_Buildbucket_ExperimentReason> get experimentReasons => $_getMap(4); - - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(9) - $core.Map<$core.String, ResolvedDataRef> get agentExecutable => $_getMap(5); - - @$pb.TagNumber(10) - BuildInfra_Buildbucket_Agent get agent => $_getN(6); - @$pb.TagNumber(10) - set agent(BuildInfra_Buildbucket_Agent v) { - setField(10, v); - } - - @$pb.TagNumber(10) - $core.bool hasAgent() => $_has(6); - @$pb.TagNumber(10) - void clearAgent() => clearField(10); - @$pb.TagNumber(10) - BuildInfra_Buildbucket_Agent ensureAgent() => $_ensure(6); - - @$pb.TagNumber(11) - $core.List<$core.String> get knownPublicGerritHosts => $_getList(7); - - @$pb.TagNumber(12) - $core.bool get buildNumber => $_getBF(8); - @$pb.TagNumber(12) - set buildNumber($core.bool v) { - $_setBool(8, v); - } - - @$pb.TagNumber(12) - $core.bool hasBuildNumber() => $_has(8); - @$pb.TagNumber(12) - void clearBuildNumber() => clearField(12); -} - -class BuildInfra_Swarming_CacheEntry extends $pb.GeneratedMessage { - factory BuildInfra_Swarming_CacheEntry() => create(); - BuildInfra_Swarming_CacheEntry._() : super(); - factory BuildInfra_Swarming_CacheEntry.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuildInfra_Swarming_CacheEntry.fromJson($core.String i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuildInfra.Swarming.CacheEntry', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'name') - ..aOS(2, _omitFieldNames ? '' : 'path') - ..aOM<$4.Duration>(3, _omitFieldNames ? '' : 'waitForWarmCache', subBuilder: $4.Duration.create) - ..aOS(4, _omitFieldNames ? '' : 'envVar') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuildInfra_Swarming_CacheEntry clone() => BuildInfra_Swarming_CacheEntry()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuildInfra_Swarming_CacheEntry copyWith(void Function(BuildInfra_Swarming_CacheEntry) updates) => - super.copyWith((message) => updates(message as BuildInfra_Swarming_CacheEntry)) as BuildInfra_Swarming_CacheEntry; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuildInfra_Swarming_CacheEntry create() => BuildInfra_Swarming_CacheEntry._(); - BuildInfra_Swarming_CacheEntry createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuildInfra_Swarming_CacheEntry getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuildInfra_Swarming_CacheEntry? _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.String get path => $_getSZ(1); - @$pb.TagNumber(2) - set path($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasPath() => $_has(1); - @$pb.TagNumber(2) - void clearPath() => clearField(2); - - @$pb.TagNumber(3) - $4.Duration get waitForWarmCache => $_getN(2); - @$pb.TagNumber(3) - set waitForWarmCache($4.Duration v) { - setField(3, v); - } - - @$pb.TagNumber(3) - $core.bool hasWaitForWarmCache() => $_has(2); - @$pb.TagNumber(3) - void clearWaitForWarmCache() => clearField(3); - @$pb.TagNumber(3) - $4.Duration ensureWaitForWarmCache() => $_ensure(2); - - @$pb.TagNumber(4) - $core.String get envVar => $_getSZ(3); - @$pb.TagNumber(4) - set envVar($core.String v) { - $_setString(3, v); - } - - @$pb.TagNumber(4) - $core.bool hasEnvVar() => $_has(3); - @$pb.TagNumber(4) - void clearEnvVar() => clearField(4); -} - -class BuildInfra_Swarming extends $pb.GeneratedMessage { - factory BuildInfra_Swarming() => create(); - BuildInfra_Swarming._() : super(); - factory BuildInfra_Swarming.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuildInfra_Swarming.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuildInfra.Swarming', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'hostname') - ..aOS(2, _omitFieldNames ? '' : 'taskId') - ..aOS(3, _omitFieldNames ? '' : 'taskServiceAccount') - ..a<$core.int>(4, _omitFieldNames ? '' : 'priority', $pb.PbFieldType.O3) - ..pc<$3.RequestedDimension>(5, _omitFieldNames ? '' : 'taskDimensions', $pb.PbFieldType.PM, - subBuilder: $3.RequestedDimension.create) - ..pc<$3.StringPair>(6, _omitFieldNames ? '' : 'botDimensions', $pb.PbFieldType.PM, subBuilder: $3.StringPair.create) - ..pc(7, _omitFieldNames ? '' : 'caches', $pb.PbFieldType.PM, - subBuilder: BuildInfra_Swarming_CacheEntry.create) - ..aOS(9, _omitFieldNames ? '' : 'parentRunId') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuildInfra_Swarming clone() => BuildInfra_Swarming()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuildInfra_Swarming copyWith(void Function(BuildInfra_Swarming) updates) => - super.copyWith((message) => updates(message as BuildInfra_Swarming)) as BuildInfra_Swarming; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuildInfra_Swarming create() => BuildInfra_Swarming._(); - BuildInfra_Swarming createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuildInfra_Swarming getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuildInfra_Swarming? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get hostname => $_getSZ(0); - @$pb.TagNumber(1) - set hostname($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasHostname() => $_has(0); - @$pb.TagNumber(1) - void clearHostname() => clearField(1); - - @$pb.TagNumber(2) - $core.String get taskId => $_getSZ(1); - @$pb.TagNumber(2) - set taskId($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasTaskId() => $_has(1); - @$pb.TagNumber(2) - void clearTaskId() => clearField(2); - - @$pb.TagNumber(3) - $core.String get taskServiceAccount => $_getSZ(2); - @$pb.TagNumber(3) - set taskServiceAccount($core.String v) { - $_setString(2, v); - } - - @$pb.TagNumber(3) - $core.bool hasTaskServiceAccount() => $_has(2); - @$pb.TagNumber(3) - void clearTaskServiceAccount() => clearField(3); - - @$pb.TagNumber(4) - $core.int get priority => $_getIZ(3); - @$pb.TagNumber(4) - set priority($core.int v) { - $_setSignedInt32(3, v); - } - - @$pb.TagNumber(4) - $core.bool hasPriority() => $_has(3); - @$pb.TagNumber(4) - void clearPriority() => clearField(4); - - @$pb.TagNumber(5) - $core.List<$3.RequestedDimension> get taskDimensions => $_getList(4); - - @$pb.TagNumber(6) - $core.List<$3.StringPair> get botDimensions => $_getList(5); - - @$pb.TagNumber(7) - $core.List get caches => $_getList(6); - - @$pb.TagNumber(9) - $core.String get parentRunId => $_getSZ(7); - @$pb.TagNumber(9) - set parentRunId($core.String v) { - $_setString(7, v); - } - - @$pb.TagNumber(9) - $core.bool hasParentRunId() => $_has(7); - @$pb.TagNumber(9) - void clearParentRunId() => clearField(9); -} - -class BuildInfra_LogDog extends $pb.GeneratedMessage { - factory BuildInfra_LogDog() => create(); - BuildInfra_LogDog._() : super(); - factory BuildInfra_LogDog.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuildInfra_LogDog.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuildInfra.LogDog', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'hostname') - ..aOS(2, _omitFieldNames ? '' : 'project') - ..aOS(3, _omitFieldNames ? '' : 'prefix') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuildInfra_LogDog clone() => BuildInfra_LogDog()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuildInfra_LogDog copyWith(void Function(BuildInfra_LogDog) updates) => - super.copyWith((message) => updates(message as BuildInfra_LogDog)) as BuildInfra_LogDog; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuildInfra_LogDog create() => BuildInfra_LogDog._(); - BuildInfra_LogDog createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuildInfra_LogDog getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuildInfra_LogDog? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get hostname => $_getSZ(0); - @$pb.TagNumber(1) - set hostname($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasHostname() => $_has(0); - @$pb.TagNumber(1) - void clearHostname() => clearField(1); - - @$pb.TagNumber(2) - $core.String get project => $_getSZ(1); - @$pb.TagNumber(2) - set project($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasProject() => $_has(1); - @$pb.TagNumber(2) - void clearProject() => clearField(2); - - @$pb.TagNumber(3) - $core.String get prefix => $_getSZ(2); - @$pb.TagNumber(3) - set prefix($core.String v) { - $_setString(2, v); - } - - @$pb.TagNumber(3) - $core.bool hasPrefix() => $_has(2); - @$pb.TagNumber(3) - void clearPrefix() => clearField(3); -} - -class BuildInfra_Recipe extends $pb.GeneratedMessage { - factory BuildInfra_Recipe() => create(); - BuildInfra_Recipe._() : super(); - factory BuildInfra_Recipe.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuildInfra_Recipe.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuildInfra.Recipe', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'cipdPackage') - ..aOS(2, _omitFieldNames ? '' : 'name') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuildInfra_Recipe clone() => BuildInfra_Recipe()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuildInfra_Recipe copyWith(void Function(BuildInfra_Recipe) updates) => - super.copyWith((message) => updates(message as BuildInfra_Recipe)) as BuildInfra_Recipe; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuildInfra_Recipe create() => BuildInfra_Recipe._(); - BuildInfra_Recipe createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuildInfra_Recipe getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuildInfra_Recipe? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get cipdPackage => $_getSZ(0); - @$pb.TagNumber(1) - set cipdPackage($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasCipdPackage() => $_has(0); - @$pb.TagNumber(1) - void clearCipdPackage() => clearField(1); - - @$pb.TagNumber(2) - $core.String get name => $_getSZ(1); - @$pb.TagNumber(2) - set name($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasName() => $_has(1); - @$pb.TagNumber(2) - void clearName() => clearField(2); -} - -class BuildInfra_ResultDB extends $pb.GeneratedMessage { - factory BuildInfra_ResultDB() => create(); - BuildInfra_ResultDB._() : super(); - factory BuildInfra_ResultDB.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuildInfra_ResultDB.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuildInfra.ResultDB', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'hostname') - ..aOS(2, _omitFieldNames ? '' : 'invocation') - ..aOB(3, _omitFieldNames ? '' : 'enable') - ..pc<$6.BigQueryExport>(4, _omitFieldNames ? '' : 'bqExports', $pb.PbFieldType.PM, - subBuilder: $6.BigQueryExport.create) - ..aOM<$6.HistoryOptions>(5, _omitFieldNames ? '' : 'historyOptions', subBuilder: $6.HistoryOptions.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuildInfra_ResultDB clone() => BuildInfra_ResultDB()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuildInfra_ResultDB copyWith(void Function(BuildInfra_ResultDB) updates) => - super.copyWith((message) => updates(message as BuildInfra_ResultDB)) as BuildInfra_ResultDB; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuildInfra_ResultDB create() => BuildInfra_ResultDB._(); - BuildInfra_ResultDB createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuildInfra_ResultDB getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuildInfra_ResultDB? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get hostname => $_getSZ(0); - @$pb.TagNumber(1) - set hostname($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasHostname() => $_has(0); - @$pb.TagNumber(1) - void clearHostname() => clearField(1); - - @$pb.TagNumber(2) - $core.String get invocation => $_getSZ(1); - @$pb.TagNumber(2) - set invocation($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasInvocation() => $_has(1); - @$pb.TagNumber(2) - void clearInvocation() => clearField(2); - - @$pb.TagNumber(3) - $core.bool get enable => $_getBF(2); - @$pb.TagNumber(3) - set enable($core.bool v) { - $_setBool(2, v); - } - - @$pb.TagNumber(3) - $core.bool hasEnable() => $_has(2); - @$pb.TagNumber(3) - void clearEnable() => clearField(3); - - @$pb.TagNumber(4) - $core.List<$6.BigQueryExport> get bqExports => $_getList(3); - - @$pb.TagNumber(5) - $6.HistoryOptions get historyOptions => $_getN(4); - @$pb.TagNumber(5) - set historyOptions($6.HistoryOptions v) { - setField(5, v); - } - - @$pb.TagNumber(5) - $core.bool hasHistoryOptions() => $_has(4); - @$pb.TagNumber(5) - void clearHistoryOptions() => clearField(5); - @$pb.TagNumber(5) - $6.HistoryOptions ensureHistoryOptions() => $_ensure(4); -} - -class BuildInfra_BBAgent_Input_CIPDPackage extends $pb.GeneratedMessage { - factory BuildInfra_BBAgent_Input_CIPDPackage() => create(); - BuildInfra_BBAgent_Input_CIPDPackage._() : super(); - factory BuildInfra_BBAgent_Input_CIPDPackage.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuildInfra_BBAgent_Input_CIPDPackage.fromJson($core.String i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuildInfra.BBAgent.Input.CIPDPackage', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'name') - ..aOS(2, _omitFieldNames ? '' : 'version') - ..aOS(3, _omitFieldNames ? '' : 'server') - ..aOS(4, _omitFieldNames ? '' : 'path') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuildInfra_BBAgent_Input_CIPDPackage clone() => BuildInfra_BBAgent_Input_CIPDPackage()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuildInfra_BBAgent_Input_CIPDPackage copyWith(void Function(BuildInfra_BBAgent_Input_CIPDPackage) updates) => - super.copyWith((message) => updates(message as BuildInfra_BBAgent_Input_CIPDPackage)) - as BuildInfra_BBAgent_Input_CIPDPackage; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuildInfra_BBAgent_Input_CIPDPackage create() => BuildInfra_BBAgent_Input_CIPDPackage._(); - BuildInfra_BBAgent_Input_CIPDPackage createEmptyInstance() => create(); - static $pb.PbList createRepeated() => - $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuildInfra_BBAgent_Input_CIPDPackage getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuildInfra_BBAgent_Input_CIPDPackage? _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.String get version => $_getSZ(1); - @$pb.TagNumber(2) - set version($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasVersion() => $_has(1); - @$pb.TagNumber(2) - void clearVersion() => clearField(2); - - @$pb.TagNumber(3) - $core.String get server => $_getSZ(2); - @$pb.TagNumber(3) - set server($core.String v) { - $_setString(2, v); - } - - @$pb.TagNumber(3) - $core.bool hasServer() => $_has(2); - @$pb.TagNumber(3) - void clearServer() => clearField(3); - - @$pb.TagNumber(4) - $core.String get path => $_getSZ(3); - @$pb.TagNumber(4) - set path($core.String v) { - $_setString(3, v); - } - - @$pb.TagNumber(4) - $core.bool hasPath() => $_has(3); - @$pb.TagNumber(4) - void clearPath() => clearField(4); -} - -class BuildInfra_BBAgent_Input extends $pb.GeneratedMessage { - factory BuildInfra_BBAgent_Input() => create(); - BuildInfra_BBAgent_Input._() : super(); - factory BuildInfra_BBAgent_Input.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuildInfra_BBAgent_Input.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuildInfra.BBAgent.Input', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..pc(1, _omitFieldNames ? '' : 'cipdPackages', $pb.PbFieldType.PM, - subBuilder: BuildInfra_BBAgent_Input_CIPDPackage.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuildInfra_BBAgent_Input clone() => BuildInfra_BBAgent_Input()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuildInfra_BBAgent_Input copyWith(void Function(BuildInfra_BBAgent_Input) updates) => - super.copyWith((message) => updates(message as BuildInfra_BBAgent_Input)) as BuildInfra_BBAgent_Input; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuildInfra_BBAgent_Input create() => BuildInfra_BBAgent_Input._(); - BuildInfra_BBAgent_Input createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuildInfra_BBAgent_Input getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuildInfra_BBAgent_Input? _defaultInstance; - - @$pb.TagNumber(1) - $core.List get cipdPackages => $_getList(0); -} - -class BuildInfra_BBAgent extends $pb.GeneratedMessage { - factory BuildInfra_BBAgent() => create(); - BuildInfra_BBAgent._() : super(); - factory BuildInfra_BBAgent.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuildInfra_BBAgent.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuildInfra.BBAgent', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'payloadPath') - ..aOS(2, _omitFieldNames ? '' : 'cacheDir') - ..pPS(3, _omitFieldNames ? '' : 'knownPublicGerritHosts') - ..aOM(4, _omitFieldNames ? '' : 'input', subBuilder: BuildInfra_BBAgent_Input.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuildInfra_BBAgent clone() => BuildInfra_BBAgent()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuildInfra_BBAgent copyWith(void Function(BuildInfra_BBAgent) updates) => - super.copyWith((message) => updates(message as BuildInfra_BBAgent)) as BuildInfra_BBAgent; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuildInfra_BBAgent create() => BuildInfra_BBAgent._(); - BuildInfra_BBAgent createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuildInfra_BBAgent getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuildInfra_BBAgent? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get payloadPath => $_getSZ(0); - @$pb.TagNumber(1) - set payloadPath($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasPayloadPath() => $_has(0); - @$pb.TagNumber(1) - void clearPayloadPath() => clearField(1); - - @$pb.TagNumber(2) - $core.String get cacheDir => $_getSZ(1); - @$pb.TagNumber(2) - set cacheDir($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasCacheDir() => $_has(1); - @$pb.TagNumber(2) - void clearCacheDir() => clearField(2); - - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(3) - $core.List<$core.String> get knownPublicGerritHosts => $_getList(2); - - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(4) - BuildInfra_BBAgent_Input get input => $_getN(3); - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(4) - set input(BuildInfra_BBAgent_Input v) { - setField(4, v); - } - - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(4) - $core.bool hasInput() => $_has(3); - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(4) - void clearInput() => clearField(4); - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(4) - BuildInfra_BBAgent_Input ensureInput() => $_ensure(3); -} - -class BuildInfra_Backend extends $pb.GeneratedMessage { - factory BuildInfra_Backend() => create(); - BuildInfra_Backend._() : super(); - factory BuildInfra_Backend.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuildInfra_Backend.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuildInfra.Backend', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOM<$5.Struct>(1, _omitFieldNames ? '' : 'config', subBuilder: $5.Struct.create) - ..aOM<$7.Task>(2, _omitFieldNames ? '' : 'task', subBuilder: $7.Task.create) - ..pc<$3.CacheEntry>(3, _omitFieldNames ? '' : 'caches', $pb.PbFieldType.PM, subBuilder: $3.CacheEntry.create) - ..pc<$3.RequestedDimension>(5, _omitFieldNames ? '' : 'taskDimensions', $pb.PbFieldType.PM, - subBuilder: $3.RequestedDimension.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuildInfra_Backend clone() => BuildInfra_Backend()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuildInfra_Backend copyWith(void Function(BuildInfra_Backend) updates) => - super.copyWith((message) => updates(message as BuildInfra_Backend)) as BuildInfra_Backend; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuildInfra_Backend create() => BuildInfra_Backend._(); - BuildInfra_Backend createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuildInfra_Backend getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuildInfra_Backend? _defaultInstance; - - @$pb.TagNumber(1) - $5.Struct get config => $_getN(0); - @$pb.TagNumber(1) - set config($5.Struct v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasConfig() => $_has(0); - @$pb.TagNumber(1) - void clearConfig() => clearField(1); - @$pb.TagNumber(1) - $5.Struct ensureConfig() => $_ensure(0); - - @$pb.TagNumber(2) - $7.Task get task => $_getN(1); - @$pb.TagNumber(2) - set task($7.Task v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasTask() => $_has(1); - @$pb.TagNumber(2) - void clearTask() => clearField(2); - @$pb.TagNumber(2) - $7.Task ensureTask() => $_ensure(1); - - @$pb.TagNumber(3) - $core.List<$3.CacheEntry> get caches => $_getList(2); - - @$pb.TagNumber(5) - $core.List<$3.RequestedDimension> get taskDimensions => $_getList(3); -} - -class BuildInfra extends $pb.GeneratedMessage { - factory BuildInfra() => create(); - BuildInfra._() : super(); - factory BuildInfra.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuildInfra.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuildInfra', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOM(1, _omitFieldNames ? '' : 'buildbucket', subBuilder: BuildInfra_Buildbucket.create) - ..aOM(2, _omitFieldNames ? '' : 'swarming', subBuilder: BuildInfra_Swarming.create) - ..aOM(3, _omitFieldNames ? '' : 'logdog', subBuilder: BuildInfra_LogDog.create) - ..aOM(4, _omitFieldNames ? '' : 'recipe', subBuilder: BuildInfra_Recipe.create) - ..aOM(5, _omitFieldNames ? '' : 'resultdb', subBuilder: BuildInfra_ResultDB.create) - ..aOM(6, _omitFieldNames ? '' : 'bbagent', subBuilder: BuildInfra_BBAgent.create) - ..aOM(7, _omitFieldNames ? '' : 'backend', subBuilder: BuildInfra_Backend.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuildInfra clone() => BuildInfra()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuildInfra copyWith(void Function(BuildInfra) updates) => - super.copyWith((message) => updates(message as BuildInfra)) as BuildInfra; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuildInfra create() => BuildInfra._(); - BuildInfra createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuildInfra getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuildInfra? _defaultInstance; - - @$pb.TagNumber(1) - BuildInfra_Buildbucket get buildbucket => $_getN(0); - @$pb.TagNumber(1) - set buildbucket(BuildInfra_Buildbucket v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasBuildbucket() => $_has(0); - @$pb.TagNumber(1) - void clearBuildbucket() => clearField(1); - @$pb.TagNumber(1) - BuildInfra_Buildbucket ensureBuildbucket() => $_ensure(0); - - @$pb.TagNumber(2) - BuildInfra_Swarming get swarming => $_getN(1); - @$pb.TagNumber(2) - set swarming(BuildInfra_Swarming v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasSwarming() => $_has(1); - @$pb.TagNumber(2) - void clearSwarming() => clearField(2); - @$pb.TagNumber(2) - BuildInfra_Swarming ensureSwarming() => $_ensure(1); - - @$pb.TagNumber(3) - BuildInfra_LogDog get logdog => $_getN(2); - @$pb.TagNumber(3) - set logdog(BuildInfra_LogDog v) { - setField(3, v); - } - - @$pb.TagNumber(3) - $core.bool hasLogdog() => $_has(2); - @$pb.TagNumber(3) - void clearLogdog() => clearField(3); - @$pb.TagNumber(3) - BuildInfra_LogDog ensureLogdog() => $_ensure(2); - - @$pb.TagNumber(4) - BuildInfra_Recipe get recipe => $_getN(3); - @$pb.TagNumber(4) - set recipe(BuildInfra_Recipe v) { - setField(4, v); - } - - @$pb.TagNumber(4) - $core.bool hasRecipe() => $_has(3); - @$pb.TagNumber(4) - void clearRecipe() => clearField(4); - @$pb.TagNumber(4) - BuildInfra_Recipe ensureRecipe() => $_ensure(3); - - @$pb.TagNumber(5) - BuildInfra_ResultDB get resultdb => $_getN(4); - @$pb.TagNumber(5) - set resultdb(BuildInfra_ResultDB v) { - setField(5, v); - } - - @$pb.TagNumber(5) - $core.bool hasResultdb() => $_has(4); - @$pb.TagNumber(5) - void clearResultdb() => clearField(5); - @$pb.TagNumber(5) - BuildInfra_ResultDB ensureResultdb() => $_ensure(4); - - @$pb.TagNumber(6) - BuildInfra_BBAgent get bbagent => $_getN(5); - @$pb.TagNumber(6) - set bbagent(BuildInfra_BBAgent v) { - setField(6, v); - } - - @$pb.TagNumber(6) - $core.bool hasBbagent() => $_has(5); - @$pb.TagNumber(6) - void clearBbagent() => clearField(6); - @$pb.TagNumber(6) - BuildInfra_BBAgent ensureBbagent() => $_ensure(5); - - @$pb.TagNumber(7) - BuildInfra_Backend get backend => $_getN(6); - @$pb.TagNumber(7) - set backend(BuildInfra_Backend v) { - setField(7, v); - } - - @$pb.TagNumber(7) - $core.bool hasBackend() => $_has(6); - @$pb.TagNumber(7) - void clearBackend() => clearField(7); - @$pb.TagNumber(7) - BuildInfra_Backend ensureBackend() => $_ensure(6); -} - -const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); -const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/build.pbenum.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/build.pbenum.dart deleted file mode 100644 index 1be6742af..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/build.pbenum.dart +++ /dev/null @@ -1,67 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/build.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:core' as $core; - -import 'package:protobuf/protobuf.dart' as $pb; - -class BuildInfra_Buildbucket_ExperimentReason extends $pb.ProtobufEnum { - static const BuildInfra_Buildbucket_ExperimentReason EXPERIMENT_REASON_UNSET = - BuildInfra_Buildbucket_ExperimentReason._(0, _omitEnumNames ? '' : 'EXPERIMENT_REASON_UNSET'); - static const BuildInfra_Buildbucket_ExperimentReason EXPERIMENT_REASON_GLOBAL_DEFAULT = - BuildInfra_Buildbucket_ExperimentReason._(1, _omitEnumNames ? '' : 'EXPERIMENT_REASON_GLOBAL_DEFAULT'); - static const BuildInfra_Buildbucket_ExperimentReason EXPERIMENT_REASON_BUILDER_CONFIG = - BuildInfra_Buildbucket_ExperimentReason._(2, _omitEnumNames ? '' : 'EXPERIMENT_REASON_BUILDER_CONFIG'); - static const BuildInfra_Buildbucket_ExperimentReason EXPERIMENT_REASON_GLOBAL_MINIMUM = - BuildInfra_Buildbucket_ExperimentReason._(3, _omitEnumNames ? '' : 'EXPERIMENT_REASON_GLOBAL_MINIMUM'); - static const BuildInfra_Buildbucket_ExperimentReason EXPERIMENT_REASON_REQUESTED = - BuildInfra_Buildbucket_ExperimentReason._(4, _omitEnumNames ? '' : 'EXPERIMENT_REASON_REQUESTED'); - static const BuildInfra_Buildbucket_ExperimentReason EXPERIMENT_REASON_GLOBAL_INACTIVE = - BuildInfra_Buildbucket_ExperimentReason._(5, _omitEnumNames ? '' : 'EXPERIMENT_REASON_GLOBAL_INACTIVE'); - - static const $core.List values = [ - EXPERIMENT_REASON_UNSET, - EXPERIMENT_REASON_GLOBAL_DEFAULT, - EXPERIMENT_REASON_BUILDER_CONFIG, - EXPERIMENT_REASON_GLOBAL_MINIMUM, - EXPERIMENT_REASON_REQUESTED, - EXPERIMENT_REASON_GLOBAL_INACTIVE, - ]; - - static final $core.Map<$core.int, BuildInfra_Buildbucket_ExperimentReason> _byValue = - $pb.ProtobufEnum.initByValue(values); - static BuildInfra_Buildbucket_ExperimentReason? valueOf($core.int value) => _byValue[value]; - - const BuildInfra_Buildbucket_ExperimentReason._($core.int v, $core.String n) : super(v, n); -} - -class BuildInfra_Buildbucket_Agent_Purpose extends $pb.ProtobufEnum { - static const BuildInfra_Buildbucket_Agent_Purpose PURPOSE_UNSPECIFIED = - BuildInfra_Buildbucket_Agent_Purpose._(0, _omitEnumNames ? '' : 'PURPOSE_UNSPECIFIED'); - static const BuildInfra_Buildbucket_Agent_Purpose PURPOSE_EXE_PAYLOAD = - BuildInfra_Buildbucket_Agent_Purpose._(1, _omitEnumNames ? '' : 'PURPOSE_EXE_PAYLOAD'); - static const BuildInfra_Buildbucket_Agent_Purpose PURPOSE_BBAGENT_UTILITY = - BuildInfra_Buildbucket_Agent_Purpose._(2, _omitEnumNames ? '' : 'PURPOSE_BBAGENT_UTILITY'); - - static const $core.List values = [ - PURPOSE_UNSPECIFIED, - PURPOSE_EXE_PAYLOAD, - PURPOSE_BBAGENT_UTILITY, - ]; - - static final $core.Map<$core.int, BuildInfra_Buildbucket_Agent_Purpose> _byValue = - $pb.ProtobufEnum.initByValue(values); - static BuildInfra_Buildbucket_Agent_Purpose? valueOf($core.int value) => _byValue[value]; - - const BuildInfra_Buildbucket_Agent_Purpose._($core.int v, $core.String n) : super(v, n); -} - -const _omitEnumNames = $core.bool.fromEnvironment('protobuf.omit_enum_names'); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/build.pbjson.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/build.pbjson.dart deleted file mode 100644 index c8ab13af5..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/build.pbjson.dart +++ /dev/null @@ -1,837 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/build.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:convert' as $convert; -import 'dart:core' as $core; -import 'dart:typed_data' as $typed_data; - -@$core.Deprecated('Use buildDescriptor instead') -const Build$json = { - '1': 'Build', - '2': [ - {'1': 'id', '3': 1, '4': 1, '5': 3, '8': {}, '10': 'id'}, - {'1': 'builder', '3': 2, '4': 1, '5': 11, '6': '.buildbucket.v2.BuilderID', '8': {}, '10': 'builder'}, - { - '1': 'builder_info', - '3': 34, - '4': 1, - '5': 11, - '6': '.buildbucket.v2.Build.BuilderInfo', - '8': {}, - '10': 'builderInfo' - }, - {'1': 'number', '3': 3, '4': 1, '5': 5, '8': {}, '10': 'number'}, - {'1': 'created_by', '3': 4, '4': 1, '5': 9, '8': {}, '10': 'createdBy'}, - {'1': 'canceled_by', '3': 23, '4': 1, '5': 9, '8': {}, '10': 'canceledBy'}, - {'1': 'create_time', '3': 6, '4': 1, '5': 11, '6': '.google.protobuf.Timestamp', '8': {}, '10': 'createTime'}, - {'1': 'start_time', '3': 7, '4': 1, '5': 11, '6': '.google.protobuf.Timestamp', '8': {}, '10': 'startTime'}, - {'1': 'end_time', '3': 8, '4': 1, '5': 11, '6': '.google.protobuf.Timestamp', '8': {}, '10': 'endTime'}, - {'1': 'update_time', '3': 9, '4': 1, '5': 11, '6': '.google.protobuf.Timestamp', '8': {}, '10': 'updateTime'}, - {'1': 'cancel_time', '3': 32, '4': 1, '5': 11, '6': '.google.protobuf.Timestamp', '8': {}, '10': 'cancelTime'}, - {'1': 'status', '3': 12, '4': 1, '5': 14, '6': '.buildbucket.v2.Status', '8': {}, '10': 'status'}, - {'1': 'summary_markdown', '3': 20, '4': 1, '5': 9, '8': {}, '10': 'summaryMarkdown'}, - {'1': 'cancellation_markdown', '3': 33, '4': 1, '5': 9, '8': {}, '10': 'cancellationMarkdown'}, - {'1': 'critical', '3': 21, '4': 1, '5': 14, '6': '.buildbucket.v2.Trinary', '8': {}, '10': 'critical'}, - { - '1': 'status_details', - '3': 22, - '4': 1, - '5': 11, - '6': '.buildbucket.v2.StatusDetails', - '8': {}, - '10': 'statusDetails' - }, - {'1': 'input', '3': 15, '4': 1, '5': 11, '6': '.buildbucket.v2.Build.Input', '8': {}, '10': 'input'}, - {'1': 'output', '3': 16, '4': 1, '5': 11, '6': '.buildbucket.v2.Build.Output', '8': {}, '10': 'output'}, - {'1': 'steps', '3': 17, '4': 3, '5': 11, '6': '.buildbucket.v2.Step', '8': {}, '10': 'steps'}, - {'1': 'infra', '3': 18, '4': 1, '5': 11, '6': '.buildbucket.v2.BuildInfra', '8': {}, '10': 'infra'}, - {'1': 'tags', '3': 19, '4': 3, '5': 11, '6': '.buildbucket.v2.StringPair', '10': 'tags'}, - {'1': 'exe', '3': 24, '4': 1, '5': 11, '6': '.buildbucket.v2.Executable', '8': {}, '10': 'exe'}, - {'1': 'canary', '3': 25, '4': 1, '5': 8, '10': 'canary'}, - {'1': 'scheduling_timeout', '3': 26, '4': 1, '5': 11, '6': '.google.protobuf.Duration', '10': 'schedulingTimeout'}, - {'1': 'execution_timeout', '3': 27, '4': 1, '5': 11, '6': '.google.protobuf.Duration', '10': 'executionTimeout'}, - {'1': 'grace_period', '3': 29, '4': 1, '5': 11, '6': '.google.protobuf.Duration', '10': 'gracePeriod'}, - {'1': 'wait_for_capacity', '3': 28, '4': 1, '5': 8, '10': 'waitForCapacity'}, - {'1': 'can_outlive_parent', '3': 30, '4': 1, '5': 8, '8': {}, '10': 'canOutliveParent'}, - {'1': 'ancestor_ids', '3': 31, '4': 3, '5': 3, '8': {}, '10': 'ancestorIds'}, - {'1': 'retriable', '3': 35, '4': 1, '5': 14, '6': '.buildbucket.v2.Trinary', '10': 'retriable'}, - ], - '3': [Build_Input$json, Build_Output$json, Build_BuilderInfo$json], - '9': [ - {'1': 5, '2': 6}, - {'1': 13, '2': 14}, - {'1': 14, '2': 15}, - ], -}; - -@$core.Deprecated('Use buildDescriptor instead') -const Build_Input$json = { - '1': 'Input', - '2': [ - {'1': 'properties', '3': 1, '4': 1, '5': 11, '6': '.google.protobuf.Struct', '10': 'properties'}, - { - '1': 'gitiles_commit', - '3': 2, - '4': 1, - '5': 11, - '6': '.buildbucket.v2.GitilesCommit', - '8': {}, - '10': 'gitilesCommit' - }, - { - '1': 'gerrit_changes', - '3': 3, - '4': 3, - '5': 11, - '6': '.buildbucket.v2.GerritChange', - '8': {}, - '10': 'gerritChanges' - }, - {'1': 'experimental', '3': 5, '4': 1, '5': 8, '10': 'experimental'}, - {'1': 'experiments', '3': 6, '4': 3, '5': 9, '10': 'experiments'}, - ], -}; - -@$core.Deprecated('Use buildDescriptor instead') -const Build_Output$json = { - '1': 'Output', - '2': [ - {'1': 'properties', '3': 1, '4': 1, '5': 11, '6': '.google.protobuf.Struct', '10': 'properties'}, - {'1': 'gitiles_commit', '3': 3, '4': 1, '5': 11, '6': '.buildbucket.v2.GitilesCommit', '10': 'gitilesCommit'}, - {'1': 'logs', '3': 5, '4': 3, '5': 11, '6': '.buildbucket.v2.Log', '10': 'logs'}, - {'1': 'status', '3': 6, '4': 1, '5': 14, '6': '.buildbucket.v2.Status', '10': 'status'}, - {'1': 'status_details', '3': 7, '4': 1, '5': 11, '6': '.buildbucket.v2.StatusDetails', '10': 'statusDetails'}, - {'1': 'summary_html', '3': 8, '4': 1, '5': 9, '10': 'summaryHtml'}, - {'1': 'summary_markdown', '3': 2, '4': 1, '5': 9, '10': 'summaryMarkdown'}, - ], - '9': [ - {'1': 4, '2': 5}, - ], -}; - -@$core.Deprecated('Use buildDescriptor instead') -const Build_BuilderInfo$json = { - '1': 'BuilderInfo', - '2': [ - {'1': 'description', '3': 1, '4': 1, '5': 9, '10': 'description'}, - ], -}; - -/// Descriptor for `Build`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List buildDescriptor = - $convert.base64Decode('CgVCdWlsZBIYCgJpZBgBIAEoA0II4EEDuM68AwNSAmlkEkAKB2J1aWxkZXIYAiABKAsyGS5idW' - 'lsZGJ1Y2tldC52Mi5CdWlsZGVySURCC4rDGgIIArjOvAMCUgdidWlsZGVyEkkKDGJ1aWxkZXJf' - 'aW5mbxgiIAEoCzIhLmJ1aWxkYnVja2V0LnYyLkJ1aWxkLkJ1aWxkZXJJbmZvQgPgQQNSC2J1aW' - 'xkZXJJbmZvEiAKBm51bWJlchgDIAEoBUII4EEDuM68AwJSBm51bWJlchIiCgpjcmVhdGVkX2J5' - 'GAQgASgJQgPgQQNSCWNyZWF0ZWRCeRIkCgtjYW5jZWxlZF9ieRgXIAEoCUID4EEDUgpjYW5jZW' - 'xlZEJ5EkUKC2NyZWF0ZV90aW1lGAYgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcEII' - '4EEDuM68AwJSCmNyZWF0ZVRpbWUSQwoKc3RhcnRfdGltZRgHIAEoCzIaLmdvb2dsZS5wcm90b2' - 'J1Zi5UaW1lc3RhbXBCCOBBA7jOvAMCUglzdGFydFRpbWUSPwoIZW5kX3RpbWUYCCABKAsyGi5n' - 'b29nbGUucHJvdG9idWYuVGltZXN0YW1wQgjgQQO4zrwDAlIHZW5kVGltZRJFCgt1cGRhdGVfdG' - 'ltZRgJIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXBCCOBBA7jOvAMCUgp1cGRhdGVU' - 'aW1lEkUKC2NhbmNlbF90aW1lGCAgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcEII4E' - 'EDuM68AwJSCmNhbmNlbFRpbWUSOwoGc3RhdHVzGAwgASgOMhYuYnVpbGRidWNrZXQudjIuU3Rh' - 'dHVzQguKwxoCCAO4zrwDA1IGc3RhdHVzEjEKEHN1bW1hcnlfbWFya2Rvd24YFCABKAlCBorDGg' - 'IIA1IPc3VtbWFyeU1hcmtkb3duEjsKFWNhbmNlbGxhdGlvbl9tYXJrZG93bhghIAEoCUIGisMa' - 'AggDUhRjYW5jZWxsYXRpb25NYXJrZG93bhI6Cghjcml0aWNhbBgVIAEoDjIXLmJ1aWxkYnVja2' - 'V0LnYyLlRyaW5hcnlCBbjOvAMCUghjcml0aWNhbBJRCg5zdGF0dXNfZGV0YWlscxgWIAEoCzId' - 'LmJ1aWxkYnVja2V0LnYyLlN0YXR1c0RldGFpbHNCC4rDGgIIA7jOvAMDUg1zdGF0dXNEZXRhaW' - 'xzEjkKBWlucHV0GA8gASgLMhsuYnVpbGRidWNrZXQudjIuQnVpbGQuSW5wdXRCBorDGgIIAlIF' - 'aW5wdXQSPAoGb3V0cHV0GBAgASgLMhwuYnVpbGRidWNrZXQudjIuQnVpbGQuT3V0cHV0QgaKwx' - 'oCCANSBm91dHB1dBIyCgVzdGVwcxgRIAMoCzIULmJ1aWxkYnVja2V0LnYyLlN0ZXBCBorDGgII' - 'A1IFc3RlcHMSOAoFaW5mcmEYEiABKAsyGi5idWlsZGJ1Y2tldC52Mi5CdWlsZEluZnJhQgaKwx' - 'oCCAJSBWluZnJhEi4KBHRhZ3MYEyADKAsyGi5idWlsZGJ1Y2tldC52Mi5TdHJpbmdQYWlyUgR0' - 'YWdzEjQKA2V4ZRgYIAEoCzIaLmJ1aWxkYnVja2V0LnYyLkV4ZWN1dGFibGVCBorDGgIIAlIDZX' - 'hlEhYKBmNhbmFyeRgZIAEoCFIGY2FuYXJ5EkgKEnNjaGVkdWxpbmdfdGltZW91dBgaIAEoCzIZ' - 'Lmdvb2dsZS5wcm90b2J1Zi5EdXJhdGlvblIRc2NoZWR1bGluZ1RpbWVvdXQSRgoRZXhlY3V0aW' - '9uX3RpbWVvdXQYGyABKAsyGS5nb29nbGUucHJvdG9idWYuRHVyYXRpb25SEGV4ZWN1dGlvblRp' - 'bWVvdXQSPAoMZ3JhY2VfcGVyaW9kGB0gASgLMhkuZ29vZ2xlLnByb3RvYnVmLkR1cmF0aW9uUg' - 'tncmFjZVBlcmlvZBIqChF3YWl0X2Zvcl9jYXBhY2l0eRgcIAEoCFIPd2FpdEZvckNhcGFjaXR5' - 'EjMKEmNhbl9vdXRsaXZlX3BhcmVudBgeIAEoCEIFuM68AwNSEGNhbk91dGxpdmVQYXJlbnQSKw' - 'oMYW5jZXN0b3JfaWRzGB8gAygDQgjgQQO4zrwDA1ILYW5jZXN0b3JJZHMSNQoJcmV0cmlhYmxl' - 'GCMgASgOMhcuYnVpbGRidWNrZXQudjIuVHJpbmFyeVIJcmV0cmlhYmxlGp8CCgVJbnB1dBI3Cg' - 'pwcm9wZXJ0aWVzGAEgASgLMhcuZ29vZ2xlLnByb3RvYnVmLlN0cnVjdFIKcHJvcGVydGllcxJL' - 'Cg5naXRpbGVzX2NvbW1pdBgCIAEoCzIdLmJ1aWxkYnVja2V0LnYyLkdpdGlsZXNDb21taXRCBb' - 'jOvAMCUg1naXRpbGVzQ29tbWl0EkoKDmdlcnJpdF9jaGFuZ2VzGAMgAygLMhwuYnVpbGRidWNr' - 'ZXQudjIuR2Vycml0Q2hhbmdlQgW4zrwDAlINZ2Vycml0Q2hhbmdlcxIiCgxleHBlcmltZW50YW' - 'wYBSABKAhSDGV4cGVyaW1lbnRhbBIgCgtleHBlcmltZW50cxgGIAMoCVILZXhwZXJpbWVudHMa' - '+gIKBk91dHB1dBI3Cgpwcm9wZXJ0aWVzGAEgASgLMhcuZ29vZ2xlLnByb3RvYnVmLlN0cnVjdF' - 'IKcHJvcGVydGllcxJECg5naXRpbGVzX2NvbW1pdBgDIAEoCzIdLmJ1aWxkYnVja2V0LnYyLkdp' - 'dGlsZXNDb21taXRSDWdpdGlsZXNDb21taXQSJwoEbG9ncxgFIAMoCzITLmJ1aWxkYnVja2V0Ln' - 'YyLkxvZ1IEbG9ncxIuCgZzdGF0dXMYBiABKA4yFi5idWlsZGJ1Y2tldC52Mi5TdGF0dXNSBnN0' - 'YXR1cxJECg5zdGF0dXNfZGV0YWlscxgHIAEoCzIdLmJ1aWxkYnVja2V0LnYyLlN0YXR1c0RldG' - 'FpbHNSDXN0YXR1c0RldGFpbHMSIQoMc3VtbWFyeV9odG1sGAggASgJUgtzdW1tYXJ5SHRtbBIp' - 'ChBzdW1tYXJ5X21hcmtkb3duGAIgASgJUg9zdW1tYXJ5TWFya2Rvd25KBAgEEAUaLwoLQnVpbG' - 'RlckluZm8SIAoLZGVzY3JpcHRpb24YASABKAlSC2Rlc2NyaXB0aW9uSgQIBRAGSgQIDRAOSgQI' - 'DhAP'); - -@$core.Deprecated('Use inputDataRefDescriptor instead') -const InputDataRef$json = { - '1': 'InputDataRef', - '2': [ - {'1': 'cas', '3': 1, '4': 1, '5': 11, '6': '.buildbucket.v2.InputDataRef.CAS', '9': 0, '10': 'cas'}, - {'1': 'cipd', '3': 2, '4': 1, '5': 11, '6': '.buildbucket.v2.InputDataRef.CIPD', '9': 0, '10': 'cipd'}, - {'1': 'on_path', '3': 3, '4': 3, '5': 9, '10': 'onPath'}, - ], - '3': [InputDataRef_CAS$json, InputDataRef_CIPD$json], - '8': [ - {'1': 'data_type'}, - ], - '9': [ - {'1': 4, '2': 5}, - ], -}; - -@$core.Deprecated('Use inputDataRefDescriptor instead') -const InputDataRef_CAS$json = { - '1': 'CAS', - '2': [ - {'1': 'cas_instance', '3': 1, '4': 1, '5': 9, '10': 'casInstance'}, - {'1': 'digest', '3': 2, '4': 1, '5': 11, '6': '.buildbucket.v2.InputDataRef.CAS.Digest', '10': 'digest'}, - ], - '3': [InputDataRef_CAS_Digest$json], -}; - -@$core.Deprecated('Use inputDataRefDescriptor instead') -const InputDataRef_CAS_Digest$json = { - '1': 'Digest', - '2': [ - {'1': 'hash', '3': 1, '4': 1, '5': 9, '10': 'hash'}, - {'1': 'size_bytes', '3': 2, '4': 1, '5': 3, '10': 'sizeBytes'}, - ], -}; - -@$core.Deprecated('Use inputDataRefDescriptor instead') -const InputDataRef_CIPD$json = { - '1': 'CIPD', - '2': [ - {'1': 'server', '3': 1, '4': 1, '5': 9, '10': 'server'}, - {'1': 'specs', '3': 2, '4': 3, '5': 11, '6': '.buildbucket.v2.InputDataRef.CIPD.PkgSpec', '10': 'specs'}, - ], - '3': [InputDataRef_CIPD_PkgSpec$json], -}; - -@$core.Deprecated('Use inputDataRefDescriptor instead') -const InputDataRef_CIPD_PkgSpec$json = { - '1': 'PkgSpec', - '2': [ - {'1': 'package', '3': 1, '4': 1, '5': 9, '10': 'package'}, - {'1': 'version', '3': 2, '4': 1, '5': 9, '10': 'version'}, - ], -}; - -/// Descriptor for `InputDataRef`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List inputDataRefDescriptor = - $convert.base64Decode('CgxJbnB1dERhdGFSZWYSNAoDY2FzGAEgASgLMiAuYnVpbGRidWNrZXQudjIuSW5wdXREYXRhUm' - 'VmLkNBU0gAUgNjYXMSNwoEY2lwZBgCIAEoCzIhLmJ1aWxkYnVja2V0LnYyLklucHV0RGF0YVJl' - 'Zi5DSVBESABSBGNpcGQSFwoHb25fcGF0aBgDIAMoCVIGb25QYXRoGqYBCgNDQVMSIQoMY2FzX2' - 'luc3RhbmNlGAEgASgJUgtjYXNJbnN0YW5jZRI/CgZkaWdlc3QYAiABKAsyJy5idWlsZGJ1Y2tl' - 'dC52Mi5JbnB1dERhdGFSZWYuQ0FTLkRpZ2VzdFIGZGlnZXN0GjsKBkRpZ2VzdBISCgRoYXNoGA' - 'EgASgJUgRoYXNoEh0KCnNpemVfYnl0ZXMYAiABKANSCXNpemVCeXRlcxqeAQoEQ0lQRBIWCgZz' - 'ZXJ2ZXIYASABKAlSBnNlcnZlchI/CgVzcGVjcxgCIAMoCzIpLmJ1aWxkYnVja2V0LnYyLklucH' - 'V0RGF0YVJlZi5DSVBELlBrZ1NwZWNSBXNwZWNzGj0KB1BrZ1NwZWMSGAoHcGFja2FnZRgBIAEo' - 'CVIHcGFja2FnZRIYCgd2ZXJzaW9uGAIgASgJUgd2ZXJzaW9uQgsKCWRhdGFfdHlwZUoECAQQBQ' - '=='); - -@$core.Deprecated('Use resolvedDataRefDescriptor instead') -const ResolvedDataRef$json = { - '1': 'ResolvedDataRef', - '2': [ - {'1': 'cas', '3': 1, '4': 1, '5': 11, '6': '.buildbucket.v2.ResolvedDataRef.CAS', '9': 0, '10': 'cas'}, - {'1': 'cipd', '3': 2, '4': 1, '5': 11, '6': '.buildbucket.v2.ResolvedDataRef.CIPD', '9': 0, '10': 'cipd'}, - ], - '3': [ResolvedDataRef_Timing$json, ResolvedDataRef_CAS$json, ResolvedDataRef_CIPD$json], - '8': [ - {'1': 'data_type'}, - ], -}; - -@$core.Deprecated('Use resolvedDataRefDescriptor instead') -const ResolvedDataRef_Timing$json = { - '1': 'Timing', - '2': [ - {'1': 'fetch_duration', '3': 1, '4': 1, '5': 11, '6': '.google.protobuf.Duration', '10': 'fetchDuration'}, - {'1': 'install_duration', '3': 2, '4': 1, '5': 11, '6': '.google.protobuf.Duration', '10': 'installDuration'}, - ], -}; - -@$core.Deprecated('Use resolvedDataRefDescriptor instead') -const ResolvedDataRef_CAS$json = { - '1': 'CAS', - '2': [ - {'1': 'timing', '3': 1, '4': 1, '5': 11, '6': '.buildbucket.v2.ResolvedDataRef.Timing', '10': 'timing'}, - ], -}; - -@$core.Deprecated('Use resolvedDataRefDescriptor instead') -const ResolvedDataRef_CIPD$json = { - '1': 'CIPD', - '2': [ - {'1': 'specs', '3': 2, '4': 3, '5': 11, '6': '.buildbucket.v2.ResolvedDataRef.CIPD.PkgSpec', '10': 'specs'}, - ], - '3': [ResolvedDataRef_CIPD_PkgSpec$json], -}; - -@$core.Deprecated('Use resolvedDataRefDescriptor instead') -const ResolvedDataRef_CIPD_PkgSpec$json = { - '1': 'PkgSpec', - '2': [ - {'1': 'skipped', '3': 1, '4': 1, '5': 8, '10': 'skipped'}, - {'1': 'package', '3': 2, '4': 1, '5': 9, '10': 'package'}, - {'1': 'version', '3': 3, '4': 1, '5': 9, '10': 'version'}, - {'1': 'was_cached', '3': 4, '4': 1, '5': 14, '6': '.buildbucket.v2.Trinary', '10': 'wasCached'}, - {'1': 'timing', '3': 5, '4': 1, '5': 11, '6': '.buildbucket.v2.ResolvedDataRef.Timing', '10': 'timing'}, - ], -}; - -/// Descriptor for `ResolvedDataRef`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List resolvedDataRefDescriptor = - $convert.base64Decode('Cg9SZXNvbHZlZERhdGFSZWYSNwoDY2FzGAEgASgLMiMuYnVpbGRidWNrZXQudjIuUmVzb2x2ZW' - 'REYXRhUmVmLkNBU0gAUgNjYXMSOgoEY2lwZBgCIAEoCzIkLmJ1aWxkYnVja2V0LnYyLlJlc29s' - 'dmVkRGF0YVJlZi5DSVBESABSBGNpcGQakAEKBlRpbWluZxJACg5mZXRjaF9kdXJhdGlvbhgBIA' - 'EoCzIZLmdvb2dsZS5wcm90b2J1Zi5EdXJhdGlvblINZmV0Y2hEdXJhdGlvbhJEChBpbnN0YWxs' - 'X2R1cmF0aW9uGAIgASgLMhkuZ29vZ2xlLnByb3RvYnVmLkR1cmF0aW9uUg9pbnN0YWxsRHVyYX' - 'Rpb24aRQoDQ0FTEj4KBnRpbWluZxgBIAEoCzImLmJ1aWxkYnVja2V0LnYyLlJlc29sdmVkRGF0' - 'YVJlZi5UaW1pbmdSBnRpbWluZxqcAgoEQ0lQRBJCCgVzcGVjcxgCIAMoCzIsLmJ1aWxkYnVja2' - 'V0LnYyLlJlc29sdmVkRGF0YVJlZi5DSVBELlBrZ1NwZWNSBXNwZWNzGs8BCgdQa2dTcGVjEhgK' - 'B3NraXBwZWQYASABKAhSB3NraXBwZWQSGAoHcGFja2FnZRgCIAEoCVIHcGFja2FnZRIYCgd2ZX' - 'JzaW9uGAMgASgJUgd2ZXJzaW9uEjYKCndhc19jYWNoZWQYBCABKA4yFy5idWlsZGJ1Y2tldC52' - 'Mi5UcmluYXJ5Ugl3YXNDYWNoZWQSPgoGdGltaW5nGAUgASgLMiYuYnVpbGRidWNrZXQudjIuUm' - 'Vzb2x2ZWREYXRhUmVmLlRpbWluZ1IGdGltaW5nQgsKCWRhdGFfdHlwZQ=='); - -@$core.Deprecated('Use buildInfraDescriptor instead') -const BuildInfra$json = { - '1': 'BuildInfra', - '2': [ - { - '1': 'buildbucket', - '3': 1, - '4': 1, - '5': 11, - '6': '.buildbucket.v2.BuildInfra.Buildbucket', - '8': {}, - '10': 'buildbucket' - }, - {'1': 'swarming', '3': 2, '4': 1, '5': 11, '6': '.buildbucket.v2.BuildInfra.Swarming', '10': 'swarming'}, - {'1': 'logdog', '3': 3, '4': 1, '5': 11, '6': '.buildbucket.v2.BuildInfra.LogDog', '8': {}, '10': 'logdog'}, - {'1': 'recipe', '3': 4, '4': 1, '5': 11, '6': '.buildbucket.v2.BuildInfra.Recipe', '10': 'recipe'}, - {'1': 'resultdb', '3': 5, '4': 1, '5': 11, '6': '.buildbucket.v2.BuildInfra.ResultDB', '8': {}, '10': 'resultdb'}, - {'1': 'bbagent', '3': 6, '4': 1, '5': 11, '6': '.buildbucket.v2.BuildInfra.BBAgent', '10': 'bbagent'}, - {'1': 'backend', '3': 7, '4': 1, '5': 11, '6': '.buildbucket.v2.BuildInfra.Backend', '10': 'backend'}, - ], - '3': [ - BuildInfra_Buildbucket$json, - BuildInfra_Swarming$json, - BuildInfra_LogDog$json, - BuildInfra_Recipe$json, - BuildInfra_ResultDB$json, - BuildInfra_BBAgent$json, - BuildInfra_Backend$json - ], -}; - -@$core.Deprecated('Use buildInfraDescriptor instead') -const BuildInfra_Buildbucket$json = { - '1': 'Buildbucket', - '2': [ - {'1': 'service_config_revision', '3': 2, '4': 1, '5': 9, '10': 'serviceConfigRevision'}, - {'1': 'requested_properties', '3': 5, '4': 1, '5': 11, '6': '.google.protobuf.Struct', '10': 'requestedProperties'}, - { - '1': 'requested_dimensions', - '3': 6, - '4': 3, - '5': 11, - '6': '.buildbucket.v2.RequestedDimension', - '10': 'requestedDimensions' - }, - {'1': 'hostname', '3': 7, '4': 1, '5': 9, '10': 'hostname'}, - { - '1': 'experiment_reasons', - '3': 8, - '4': 3, - '5': 11, - '6': '.buildbucket.v2.BuildInfra.Buildbucket.ExperimentReasonsEntry', - '10': 'experimentReasons' - }, - { - '1': 'agent_executable', - '3': 9, - '4': 3, - '5': 11, - '6': '.buildbucket.v2.BuildInfra.Buildbucket.AgentExecutableEntry', - '8': {'3': true}, - '10': 'agentExecutable', - }, - { - '1': 'agent', - '3': 10, - '4': 1, - '5': 11, - '6': '.buildbucket.v2.BuildInfra.Buildbucket.Agent', - '8': {}, - '10': 'agent' - }, - {'1': 'known_public_gerrit_hosts', '3': 11, '4': 3, '5': 9, '10': 'knownPublicGerritHosts'}, - {'1': 'build_number', '3': 12, '4': 1, '5': 8, '10': 'buildNumber'}, - ], - '3': [ - BuildInfra_Buildbucket_Agent$json, - BuildInfra_Buildbucket_ExperimentReasonsEntry$json, - BuildInfra_Buildbucket_AgentExecutableEntry$json - ], - '4': [BuildInfra_Buildbucket_ExperimentReason$json], - '9': [ - {'1': 4, '2': 5}, - ], -}; - -@$core.Deprecated('Use buildInfraDescriptor instead') -const BuildInfra_Buildbucket_Agent$json = { - '1': 'Agent', - '2': [ - { - '1': 'input', - '3': 1, - '4': 1, - '5': 11, - '6': '.buildbucket.v2.BuildInfra.Buildbucket.Agent.Input', - '8': {}, - '10': 'input' - }, - { - '1': 'output', - '3': 2, - '4': 1, - '5': 11, - '6': '.buildbucket.v2.BuildInfra.Buildbucket.Agent.Output', - '8': {}, - '10': 'output' - }, - { - '1': 'source', - '3': 3, - '4': 1, - '5': 11, - '6': '.buildbucket.v2.BuildInfra.Buildbucket.Agent.Source', - '8': {}, - '10': 'source' - }, - { - '1': 'purposes', - '3': 4, - '4': 3, - '5': 11, - '6': '.buildbucket.v2.BuildInfra.Buildbucket.Agent.PurposesEntry', - '10': 'purposes' - }, - ], - '3': [ - BuildInfra_Buildbucket_Agent_Source$json, - BuildInfra_Buildbucket_Agent_Input$json, - BuildInfra_Buildbucket_Agent_Output$json, - BuildInfra_Buildbucket_Agent_PurposesEntry$json - ], - '4': [BuildInfra_Buildbucket_Agent_Purpose$json], -}; - -@$core.Deprecated('Use buildInfraDescriptor instead') -const BuildInfra_Buildbucket_Agent_Source$json = { - '1': 'Source', - '2': [ - { - '1': 'cipd', - '3': 1, - '4': 1, - '5': 11, - '6': '.buildbucket.v2.BuildInfra.Buildbucket.Agent.Source.CIPD', - '9': 0, - '10': 'cipd' - }, - ], - '3': [BuildInfra_Buildbucket_Agent_Source_CIPD$json], - '8': [ - {'1': 'data_type'}, - ], -}; - -@$core.Deprecated('Use buildInfraDescriptor instead') -const BuildInfra_Buildbucket_Agent_Source_CIPD$json = { - '1': 'CIPD', - '2': [ - {'1': 'package', '3': 1, '4': 1, '5': 9, '10': 'package'}, - {'1': 'version', '3': 2, '4': 1, '5': 9, '10': 'version'}, - {'1': 'server', '3': 3, '4': 1, '5': 9, '10': 'server'}, - { - '1': 'resolved_instances', - '3': 4, - '4': 3, - '5': 11, - '6': '.buildbucket.v2.BuildInfra.Buildbucket.Agent.Source.CIPD.ResolvedInstancesEntry', - '8': {}, - '10': 'resolvedInstances' - }, - ], - '3': [BuildInfra_Buildbucket_Agent_Source_CIPD_ResolvedInstancesEntry$json], -}; - -@$core.Deprecated('Use buildInfraDescriptor instead') -const BuildInfra_Buildbucket_Agent_Source_CIPD_ResolvedInstancesEntry$json = { - '1': 'ResolvedInstancesEntry', - '2': [ - {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, - {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'}, - ], - '7': {'7': true}, -}; - -@$core.Deprecated('Use buildInfraDescriptor instead') -const BuildInfra_Buildbucket_Agent_Input$json = { - '1': 'Input', - '2': [ - { - '1': 'data', - '3': 1, - '4': 3, - '5': 11, - '6': '.buildbucket.v2.BuildInfra.Buildbucket.Agent.Input.DataEntry', - '10': 'data' - }, - ], - '3': [BuildInfra_Buildbucket_Agent_Input_DataEntry$json], -}; - -@$core.Deprecated('Use buildInfraDescriptor instead') -const BuildInfra_Buildbucket_Agent_Input_DataEntry$json = { - '1': 'DataEntry', - '2': [ - {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, - {'1': 'value', '3': 2, '4': 1, '5': 11, '6': '.buildbucket.v2.InputDataRef', '10': 'value'}, - ], - '7': {'7': true}, -}; - -@$core.Deprecated('Use buildInfraDescriptor instead') -const BuildInfra_Buildbucket_Agent_Output$json = { - '1': 'Output', - '2': [ - { - '1': 'resolved_data', - '3': 1, - '4': 3, - '5': 11, - '6': '.buildbucket.v2.BuildInfra.Buildbucket.Agent.Output.ResolvedDataEntry', - '10': 'resolvedData' - }, - {'1': 'status', '3': 2, '4': 1, '5': 14, '6': '.buildbucket.v2.Status', '10': 'status'}, - {'1': 'status_details', '3': 3, '4': 1, '5': 11, '6': '.buildbucket.v2.StatusDetails', '10': 'statusDetails'}, - {'1': 'summary_html', '3': 4, '4': 1, '5': 9, '10': 'summaryHtml'}, - {'1': 'agent_platform', '3': 5, '4': 1, '5': 9, '10': 'agentPlatform'}, - {'1': 'total_duration', '3': 6, '4': 1, '5': 11, '6': '.google.protobuf.Duration', '10': 'totalDuration'}, - ], - '3': [BuildInfra_Buildbucket_Agent_Output_ResolvedDataEntry$json], -}; - -@$core.Deprecated('Use buildInfraDescriptor instead') -const BuildInfra_Buildbucket_Agent_Output_ResolvedDataEntry$json = { - '1': 'ResolvedDataEntry', - '2': [ - {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, - {'1': 'value', '3': 2, '4': 1, '5': 11, '6': '.buildbucket.v2.ResolvedDataRef', '10': 'value'}, - ], - '7': {'7': true}, -}; - -@$core.Deprecated('Use buildInfraDescriptor instead') -const BuildInfra_Buildbucket_Agent_PurposesEntry$json = { - '1': 'PurposesEntry', - '2': [ - {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, - {'1': 'value', '3': 2, '4': 1, '5': 14, '6': '.buildbucket.v2.BuildInfra.Buildbucket.Agent.Purpose', '10': 'value'}, - ], - '7': {'7': true}, -}; - -@$core.Deprecated('Use buildInfraDescriptor instead') -const BuildInfra_Buildbucket_Agent_Purpose$json = { - '1': 'Purpose', - '2': [ - {'1': 'PURPOSE_UNSPECIFIED', '2': 0}, - {'1': 'PURPOSE_EXE_PAYLOAD', '2': 1}, - {'1': 'PURPOSE_BBAGENT_UTILITY', '2': 2}, - ], -}; - -@$core.Deprecated('Use buildInfraDescriptor instead') -const BuildInfra_Buildbucket_ExperimentReasonsEntry$json = { - '1': 'ExperimentReasonsEntry', - '2': [ - {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, - { - '1': 'value', - '3': 2, - '4': 1, - '5': 14, - '6': '.buildbucket.v2.BuildInfra.Buildbucket.ExperimentReason', - '10': 'value' - }, - ], - '7': {'7': true}, -}; - -@$core.Deprecated('Use buildInfraDescriptor instead') -const BuildInfra_Buildbucket_AgentExecutableEntry$json = { - '1': 'AgentExecutableEntry', - '2': [ - {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, - {'1': 'value', '3': 2, '4': 1, '5': 11, '6': '.buildbucket.v2.ResolvedDataRef', '10': 'value'}, - ], - '7': {'7': true}, -}; - -@$core.Deprecated('Use buildInfraDescriptor instead') -const BuildInfra_Buildbucket_ExperimentReason$json = { - '1': 'ExperimentReason', - '2': [ - {'1': 'EXPERIMENT_REASON_UNSET', '2': 0}, - {'1': 'EXPERIMENT_REASON_GLOBAL_DEFAULT', '2': 1}, - {'1': 'EXPERIMENT_REASON_BUILDER_CONFIG', '2': 2}, - {'1': 'EXPERIMENT_REASON_GLOBAL_MINIMUM', '2': 3}, - {'1': 'EXPERIMENT_REASON_REQUESTED', '2': 4}, - {'1': 'EXPERIMENT_REASON_GLOBAL_INACTIVE', '2': 5}, - ], -}; - -@$core.Deprecated('Use buildInfraDescriptor instead') -const BuildInfra_Swarming$json = { - '1': 'Swarming', - '2': [ - {'1': 'hostname', '3': 1, '4': 1, '5': 9, '8': {}, '10': 'hostname'}, - {'1': 'task_id', '3': 2, '4': 1, '5': 9, '8': {}, '10': 'taskId'}, - {'1': 'parent_run_id', '3': 9, '4': 1, '5': 9, '10': 'parentRunId'}, - {'1': 'task_service_account', '3': 3, '4': 1, '5': 9, '10': 'taskServiceAccount'}, - {'1': 'priority', '3': 4, '4': 1, '5': 5, '10': 'priority'}, - { - '1': 'task_dimensions', - '3': 5, - '4': 3, - '5': 11, - '6': '.buildbucket.v2.RequestedDimension', - '10': 'taskDimensions' - }, - {'1': 'bot_dimensions', '3': 6, '4': 3, '5': 11, '6': '.buildbucket.v2.StringPair', '10': 'botDimensions'}, - {'1': 'caches', '3': 7, '4': 3, '5': 11, '6': '.buildbucket.v2.BuildInfra.Swarming.CacheEntry', '10': 'caches'}, - ], - '3': [BuildInfra_Swarming_CacheEntry$json], -}; - -@$core.Deprecated('Use buildInfraDescriptor instead') -const BuildInfra_Swarming_CacheEntry$json = { - '1': 'CacheEntry', - '2': [ - {'1': 'name', '3': 1, '4': 1, '5': 9, '10': 'name'}, - {'1': 'path', '3': 2, '4': 1, '5': 9, '10': 'path'}, - {'1': 'wait_for_warm_cache', '3': 3, '4': 1, '5': 11, '6': '.google.protobuf.Duration', '10': 'waitForWarmCache'}, - {'1': 'env_var', '3': 4, '4': 1, '5': 9, '10': 'envVar'}, - ], -}; - -@$core.Deprecated('Use buildInfraDescriptor instead') -const BuildInfra_LogDog$json = { - '1': 'LogDog', - '2': [ - {'1': 'hostname', '3': 1, '4': 1, '5': 9, '8': {}, '10': 'hostname'}, - {'1': 'project', '3': 2, '4': 1, '5': 9, '10': 'project'}, - {'1': 'prefix', '3': 3, '4': 1, '5': 9, '10': 'prefix'}, - ], -}; - -@$core.Deprecated('Use buildInfraDescriptor instead') -const BuildInfra_Recipe$json = { - '1': 'Recipe', - '2': [ - {'1': 'cipd_package', '3': 1, '4': 1, '5': 9, '10': 'cipdPackage'}, - {'1': 'name', '3': 2, '4': 1, '5': 9, '10': 'name'}, - ], -}; - -@$core.Deprecated('Use buildInfraDescriptor instead') -const BuildInfra_ResultDB$json = { - '1': 'ResultDB', - '2': [ - {'1': 'hostname', '3': 1, '4': 1, '5': 9, '8': {}, '10': 'hostname'}, - {'1': 'invocation', '3': 2, '4': 1, '5': 9, '8': {}, '10': 'invocation'}, - {'1': 'enable', '3': 3, '4': 1, '5': 8, '10': 'enable'}, - {'1': 'bq_exports', '3': 4, '4': 3, '5': 11, '6': '.luci.resultdb.v1.BigQueryExport', '10': 'bqExports'}, - {'1': 'history_options', '3': 5, '4': 1, '5': 11, '6': '.luci.resultdb.v1.HistoryOptions', '10': 'historyOptions'}, - ], -}; - -@$core.Deprecated('Use buildInfraDescriptor instead') -const BuildInfra_BBAgent$json = { - '1': 'BBAgent', - '2': [ - {'1': 'payload_path', '3': 1, '4': 1, '5': 9, '10': 'payloadPath'}, - {'1': 'cache_dir', '3': 2, '4': 1, '5': 9, '10': 'cacheDir'}, - { - '1': 'known_public_gerrit_hosts', - '3': 3, - '4': 3, - '5': 9, - '8': {'3': true}, - '10': 'knownPublicGerritHosts', - }, - { - '1': 'input', - '3': 4, - '4': 1, - '5': 11, - '6': '.buildbucket.v2.BuildInfra.BBAgent.Input', - '8': {'3': true}, - '10': 'input', - }, - ], - '3': [BuildInfra_BBAgent_Input$json], -}; - -@$core.Deprecated('Use buildInfraDescriptor instead') -const BuildInfra_BBAgent_Input$json = { - '1': 'Input', - '2': [ - { - '1': 'cipd_packages', - '3': 1, - '4': 3, - '5': 11, - '6': '.buildbucket.v2.BuildInfra.BBAgent.Input.CIPDPackage', - '10': 'cipdPackages' - }, - ], - '3': [BuildInfra_BBAgent_Input_CIPDPackage$json], -}; - -@$core.Deprecated('Use buildInfraDescriptor instead') -const BuildInfra_BBAgent_Input_CIPDPackage$json = { - '1': 'CIPDPackage', - '2': [ - {'1': 'name', '3': 1, '4': 1, '5': 9, '10': 'name'}, - {'1': 'version', '3': 2, '4': 1, '5': 9, '10': 'version'}, - {'1': 'server', '3': 3, '4': 1, '5': 9, '10': 'server'}, - {'1': 'path', '3': 4, '4': 1, '5': 9, '10': 'path'}, - ], -}; - -@$core.Deprecated('Use buildInfraDescriptor instead') -const BuildInfra_Backend$json = { - '1': 'Backend', - '2': [ - {'1': 'config', '3': 1, '4': 1, '5': 11, '6': '.google.protobuf.Struct', '10': 'config'}, - {'1': 'task', '3': 2, '4': 1, '5': 11, '6': '.buildbucket.v2.Task', '10': 'task'}, - {'1': 'caches', '3': 3, '4': 3, '5': 11, '6': '.buildbucket.v2.CacheEntry', '10': 'caches'}, - { - '1': 'task_dimensions', - '3': 5, - '4': 3, - '5': 11, - '6': '.buildbucket.v2.RequestedDimension', - '10': 'taskDimensions' - }, - ], -}; - -/// Descriptor for `BuildInfra`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List buildInfraDescriptor = - $convert.base64Decode('CgpCdWlsZEluZnJhElAKC2J1aWxkYnVja2V0GAEgASgLMiYuYnVpbGRidWNrZXQudjIuQnVpbG' - 'RJbmZyYS5CdWlsZGJ1Y2tldEIGisMaAggCUgtidWlsZGJ1Y2tldBI/Cghzd2FybWluZxgCIAEo' - 'CzIjLmJ1aWxkYnVja2V0LnYyLkJ1aWxkSW5mcmEuU3dhcm1pbmdSCHN3YXJtaW5nEkEKBmxvZ2' - 'RvZxgDIAEoCzIhLmJ1aWxkYnVja2V0LnYyLkJ1aWxkSW5mcmEuTG9nRG9nQgaKwxoCCAJSBmxv' - 'Z2RvZxI5CgZyZWNpcGUYBCABKAsyIS5idWlsZGJ1Y2tldC52Mi5CdWlsZEluZnJhLlJlY2lwZV' - 'IGcmVjaXBlEkYKCHJlc3VsdGRiGAUgASgLMiMuYnVpbGRidWNrZXQudjIuQnVpbGRJbmZyYS5S' - 'ZXN1bHREQkIFuM68AwJSCHJlc3VsdGRiEjwKB2JiYWdlbnQYBiABKAsyIi5idWlsZGJ1Y2tldC' - '52Mi5CdWlsZEluZnJhLkJCQWdlbnRSB2JiYWdlbnQSPAoHYmFja2VuZBgHIAEoCzIiLmJ1aWxk' - 'YnVja2V0LnYyLkJ1aWxkSW5mcmEuQmFja2VuZFIHYmFja2VuZBqgFQoLQnVpbGRidWNrZXQSNg' - 'oXc2VydmljZV9jb25maWdfcmV2aXNpb24YAiABKAlSFXNlcnZpY2VDb25maWdSZXZpc2lvbhJK' - 'ChRyZXF1ZXN0ZWRfcHJvcGVydGllcxgFIAEoCzIXLmdvb2dsZS5wcm90b2J1Zi5TdHJ1Y3RSE3' - 'JlcXVlc3RlZFByb3BlcnRpZXMSVQoUcmVxdWVzdGVkX2RpbWVuc2lvbnMYBiADKAsyIi5idWls' - 'ZGJ1Y2tldC52Mi5SZXF1ZXN0ZWREaW1lbnNpb25SE3JlcXVlc3RlZERpbWVuc2lvbnMSGgoIaG' - '9zdG5hbWUYByABKAlSCGhvc3RuYW1lEmwKEmV4cGVyaW1lbnRfcmVhc29ucxgIIAMoCzI9LmJ1' - 'aWxkYnVja2V0LnYyLkJ1aWxkSW5mcmEuQnVpbGRidWNrZXQuRXhwZXJpbWVudFJlYXNvbnNFbn' - 'RyeVIRZXhwZXJpbWVudFJlYXNvbnMSagoQYWdlbnRfZXhlY3V0YWJsZRgJIAMoCzI7LmJ1aWxk' - 'YnVja2V0LnYyLkJ1aWxkSW5mcmEuQnVpbGRidWNrZXQuQWdlbnRFeGVjdXRhYmxlRW50cnlCAh' - 'gBUg9hZ2VudEV4ZWN1dGFibGUSSgoFYWdlbnQYCiABKAsyLC5idWlsZGJ1Y2tldC52Mi5CdWls' - 'ZEluZnJhLkJ1aWxkYnVja2V0LkFnZW50QgaKwxoCCAJSBWFnZW50EjkKGWtub3duX3B1YmxpY1' - '9nZXJyaXRfaG9zdHMYCyADKAlSFmtub3duUHVibGljR2Vycml0SG9zdHMSIQoMYnVpbGRfbnVt' - 'YmVyGAwgASgIUgtidWlsZE51bWJlchq/DAoFQWdlbnQSUAoFaW5wdXQYASABKAsyMi5idWlsZG' - 'J1Y2tldC52Mi5CdWlsZEluZnJhLkJ1aWxkYnVja2V0LkFnZW50LklucHV0QgaKwxoCCAJSBWlu' - 'cHV0ElMKBm91dHB1dBgCIAEoCzIzLmJ1aWxkYnVja2V0LnYyLkJ1aWxkSW5mcmEuQnVpbGRidW' - 'NrZXQuQWdlbnQuT3V0cHV0QgaKwxoCCANSBm91dHB1dBJTCgZzb3VyY2UYAyABKAsyMy5idWls' - 'ZGJ1Y2tldC52Mi5CdWlsZEluZnJhLkJ1aWxkYnVja2V0LkFnZW50LlNvdXJjZUIGisMaAggCUg' - 'Zzb3VyY2USVgoIcHVycG9zZXMYBCADKAsyOi5idWlsZGJ1Y2tldC52Mi5CdWlsZEluZnJhLkJ1' - 'aWxkYnVja2V0LkFnZW50LlB1cnBvc2VzRW50cnlSCHB1cnBvc2VzGoYDCgZTb3VyY2USTgoEY2' - 'lwZBgBIAEoCzI4LmJ1aWxkYnVja2V0LnYyLkJ1aWxkSW5mcmEuQnVpbGRidWNrZXQuQWdlbnQu' - 'U291cmNlLkNJUERIAFIEY2lwZBqeAgoEQ0lQRBIYCgdwYWNrYWdlGAEgASgJUgdwYWNrYWdlEh' - 'gKB3ZlcnNpb24YAiABKAlSB3ZlcnNpb24SFgoGc2VydmVyGAMgASgJUgZzZXJ2ZXISgwEKEnJl' - 'c29sdmVkX2luc3RhbmNlcxgEIAMoCzJPLmJ1aWxkYnVja2V0LnYyLkJ1aWxkSW5mcmEuQnVpbG' - 'RidWNrZXQuQWdlbnQuU291cmNlLkNJUEQuUmVzb2x2ZWRJbnN0YW5jZXNFbnRyeUID4EEDUhFy' - 'ZXNvbHZlZEluc3RhbmNlcxpEChZSZXNvbHZlZEluc3RhbmNlc0VudHJ5EhAKA2tleRgBIAEoCV' - 'IDa2V5EhQKBXZhbHVlGAIgASgJUgV2YWx1ZToCOAFCCwoJZGF0YV90eXBlGrABCgVJbnB1dBJQ' - 'CgRkYXRhGAEgAygLMjwuYnVpbGRidWNrZXQudjIuQnVpbGRJbmZyYS5CdWlsZGJ1Y2tldC5BZ2' - 'VudC5JbnB1dC5EYXRhRW50cnlSBGRhdGEaVQoJRGF0YUVudHJ5EhAKA2tleRgBIAEoCVIDa2V5' - 'EjIKBXZhbHVlGAIgASgLMhwuYnVpbGRidWNrZXQudjIuSW5wdXREYXRhUmVmUgV2YWx1ZToCOA' - 'Ea2AMKBk91dHB1dBJqCg1yZXNvbHZlZF9kYXRhGAEgAygLMkUuYnVpbGRidWNrZXQudjIuQnVp' - 'bGRJbmZyYS5CdWlsZGJ1Y2tldC5BZ2VudC5PdXRwdXQuUmVzb2x2ZWREYXRhRW50cnlSDHJlc2' - '9sdmVkRGF0YRIuCgZzdGF0dXMYAiABKA4yFi5idWlsZGJ1Y2tldC52Mi5TdGF0dXNSBnN0YXR1' - 'cxJECg5zdGF0dXNfZGV0YWlscxgDIAEoCzIdLmJ1aWxkYnVja2V0LnYyLlN0YXR1c0RldGFpbH' - 'NSDXN0YXR1c0RldGFpbHMSIQoMc3VtbWFyeV9odG1sGAQgASgJUgtzdW1tYXJ5SHRtbBIlCg5h' - 'Z2VudF9wbGF0Zm9ybRgFIAEoCVINYWdlbnRQbGF0Zm9ybRJACg50b3RhbF9kdXJhdGlvbhgGIA' - 'EoCzIZLmdvb2dsZS5wcm90b2J1Zi5EdXJhdGlvblINdG90YWxEdXJhdGlvbhpgChFSZXNvbHZl' - 'ZERhdGFFbnRyeRIQCgNrZXkYASABKAlSA2tleRI1CgV2YWx1ZRgCIAEoCzIfLmJ1aWxkYnVja2' - 'V0LnYyLlJlc29sdmVkRGF0YVJlZlIFdmFsdWU6AjgBGnEKDVB1cnBvc2VzRW50cnkSEAoDa2V5' - 'GAEgASgJUgNrZXkSSgoFdmFsdWUYAiABKA4yNC5idWlsZGJ1Y2tldC52Mi5CdWlsZEluZnJhLk' - 'J1aWxkYnVja2V0LkFnZW50LlB1cnBvc2VSBXZhbHVlOgI4ASJYCgdQdXJwb3NlEhcKE1BVUlBP' - 'U0VfVU5TUEVDSUZJRUQQABIXChNQVVJQT1NFX0VYRV9QQVlMT0FEEAESGwoXUFVSUE9TRV9CQk' - 'FHRU5UX1VUSUxJVFkQAhp9ChZFeHBlcmltZW50UmVhc29uc0VudHJ5EhAKA2tleRgBIAEoCVID' - 'a2V5Ek0KBXZhbHVlGAIgASgOMjcuYnVpbGRidWNrZXQudjIuQnVpbGRJbmZyYS5CdWlsZGJ1Y2' - 'tldC5FeHBlcmltZW50UmVhc29uUgV2YWx1ZToCOAEaYwoUQWdlbnRFeGVjdXRhYmxlRW50cnkS' - 'EAoDa2V5GAEgASgJUgNrZXkSNQoFdmFsdWUYAiABKAsyHy5idWlsZGJ1Y2tldC52Mi5SZXNvbH' - 'ZlZERhdGFSZWZSBXZhbHVlOgI4ASLpAQoQRXhwZXJpbWVudFJlYXNvbhIbChdFWFBFUklNRU5U' - 'X1JFQVNPTl9VTlNFVBAAEiQKIEVYUEVSSU1FTlRfUkVBU09OX0dMT0JBTF9ERUZBVUxUEAESJA' - 'ogRVhQRVJJTUVOVF9SRUFTT05fQlVJTERFUl9DT05GSUcQAhIkCiBFWFBFUklNRU5UX1JFQVNP' - 'Tl9HTE9CQUxfTUlOSU1VTRADEh8KG0VYUEVSSU1FTlRfUkVBU09OX1JFUVVFU1RFRBAEEiUKIU' - 'VYUEVSSU1FTlRfUkVBU09OX0dMT0JBTF9JTkFDVElWRRAFSgQIBBAFGrAECghTd2FybWluZxIi' - 'Cghob3N0bmFtZRgBIAEoCUIGisMaAggCUghob3N0bmFtZRIcCgd0YXNrX2lkGAIgASgJQgPgQQ' - 'NSBnRhc2tJZBIiCg1wYXJlbnRfcnVuX2lkGAkgASgJUgtwYXJlbnRSdW5JZBIwChR0YXNrX3Nl' - 'cnZpY2VfYWNjb3VudBgDIAEoCVISdGFza1NlcnZpY2VBY2NvdW50EhoKCHByaW9yaXR5GAQgAS' - 'gFUghwcmlvcml0eRJLCg90YXNrX2RpbWVuc2lvbnMYBSADKAsyIi5idWlsZGJ1Y2tldC52Mi5S' - 'ZXF1ZXN0ZWREaW1lbnNpb25SDnRhc2tEaW1lbnNpb25zEkEKDmJvdF9kaW1lbnNpb25zGAYgAy' - 'gLMhouYnVpbGRidWNrZXQudjIuU3RyaW5nUGFpclINYm90RGltZW5zaW9ucxJGCgZjYWNoZXMY' - 'ByADKAsyLi5idWlsZGJ1Y2tldC52Mi5CdWlsZEluZnJhLlN3YXJtaW5nLkNhY2hlRW50cnlSBm' - 'NhY2hlcxqXAQoKQ2FjaGVFbnRyeRISCgRuYW1lGAEgASgJUgRuYW1lEhIKBHBhdGgYAiABKAlS' - 'BHBhdGgSSAoTd2FpdF9mb3Jfd2FybV9jYWNoZRgDIAEoCzIZLmdvb2dsZS5wcm90b2J1Zi5EdX' - 'JhdGlvblIQd2FpdEZvcldhcm1DYWNoZRIXCgdlbnZfdmFyGAQgASgJUgZlbnZWYXIaXgoGTG9n' - 'RG9nEiIKCGhvc3RuYW1lGAEgASgJQgaKwxoCCAJSCGhvc3RuYW1lEhgKB3Byb2plY3QYAiABKA' - 'lSB3Byb2plY3QSFgoGcHJlZml4GAMgASgJUgZwcmVmaXgaPwoGUmVjaXBlEiEKDGNpcGRfcGFj' - 'a2FnZRgBIAEoCVILY2lwZFBhY2thZ2USEgoEbmFtZRgCIAEoCVIEbmFtZRr3AQoIUmVzdWx0RE' - 'ISIgoIaG9zdG5hbWUYASABKAlCBorDGgIIAlIIaG9zdG5hbWUSIwoKaW52b2NhdGlvbhgCIAEo' - 'CUID4EEDUgppbnZvY2F0aW9uEhYKBmVuYWJsZRgDIAEoCFIGZW5hYmxlEj8KCmJxX2V4cG9ydH' - 'MYBCADKAsyIC5sdWNpLnJlc3VsdGRiLnYxLkJpZ1F1ZXJ5RXhwb3J0UglicUV4cG9ydHMSSQoP' - 'aGlzdG9yeV9vcHRpb25zGAUgASgLMiAubHVjaS5yZXN1bHRkYi52MS5IaXN0b3J5T3B0aW9uc1' - 'IOaGlzdG9yeU9wdGlvbnMamgMKB0JCQWdlbnQSIQoMcGF5bG9hZF9wYXRoGAEgASgJUgtwYXls' - 'b2FkUGF0aBIbCgljYWNoZV9kaXIYAiABKAlSCGNhY2hlRGlyEj0KGWtub3duX3B1YmxpY19nZX' - 'JyaXRfaG9zdHMYAyADKAlCAhgBUhZrbm93blB1YmxpY0dlcnJpdEhvc3RzEkIKBWlucHV0GAQg' - 'ASgLMiguYnVpbGRidWNrZXQudjIuQnVpbGRJbmZyYS5CQkFnZW50LklucHV0QgIYAVIFaW5wdX' - 'QaywEKBUlucHV0ElkKDWNpcGRfcGFja2FnZXMYASADKAsyNC5idWlsZGJ1Y2tldC52Mi5CdWls' - 'ZEluZnJhLkJCQWdlbnQuSW5wdXQuQ0lQRFBhY2thZ2VSDGNpcGRQYWNrYWdlcxpnCgtDSVBEUG' - 'Fja2FnZRISCgRuYW1lGAEgASgJUgRuYW1lEhgKB3ZlcnNpb24YAiABKAlSB3ZlcnNpb24SFgoG' - 'c2VydmVyGAMgASgJUgZzZXJ2ZXISEgoEcGF0aBgEIAEoCVIEcGF0aBrlAQoHQmFja2VuZBIvCg' - 'Zjb25maWcYASABKAsyFy5nb29nbGUucHJvdG9idWYuU3RydWN0UgZjb25maWcSKAoEdGFzaxgC' - 'IAEoCzIULmJ1aWxkYnVja2V0LnYyLlRhc2tSBHRhc2sSMgoGY2FjaGVzGAMgAygLMhouYnVpbG' - 'RidWNrZXQudjIuQ2FjaGVFbnRyeVIGY2FjaGVzEksKD3Rhc2tfZGltZW5zaW9ucxgFIAMoCzIi' - 'LmJ1aWxkYnVja2V0LnYyLlJlcXVlc3RlZERpbWVuc2lvblIOdGFza0RpbWVuc2lvbnM='); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/build.pbserver.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/build.pbserver.dart deleted file mode 100644 index 1d487d9f9..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/build.pbserver.dart +++ /dev/null @@ -1,8 +0,0 @@ -/// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/build.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.pb.dart'; diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_common.pb.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_common.pb.dart deleted file mode 100644 index 4fb15e5cf..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_common.pb.dart +++ /dev/null @@ -1,233 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/builder_common.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:core' as $core; - -import 'package:protobuf/protobuf.dart' as $pb; - -import 'common.pb.dart' as $0; -import 'project_config.pb.dart' as $1; - -class BuilderID extends $pb.GeneratedMessage { - factory BuilderID() => create(); - BuilderID._() : super(); - factory BuilderID.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuilderID.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuilderID', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'project') - ..aOS(2, _omitFieldNames ? '' : 'bucket') - ..aOS(3, _omitFieldNames ? '' : 'builder') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuilderID clone() => BuilderID()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuilderID copyWith(void Function(BuilderID) updates) => - super.copyWith((message) => updates(message as BuilderID)) as BuilderID; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuilderID create() => BuilderID._(); - BuilderID createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuilderID getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuilderID? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get project => $_getSZ(0); - @$pb.TagNumber(1) - set project($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasProject() => $_has(0); - @$pb.TagNumber(1) - void clearProject() => clearField(1); - - @$pb.TagNumber(2) - $core.String get bucket => $_getSZ(1); - @$pb.TagNumber(2) - set bucket($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasBucket() => $_has(1); - @$pb.TagNumber(2) - void clearBucket() => clearField(2); - - @$pb.TagNumber(3) - $core.String get builder => $_getSZ(2); - @$pb.TagNumber(3) - set builder($core.String v) { - $_setString(2, v); - } - - @$pb.TagNumber(3) - $core.bool hasBuilder() => $_has(2); - @$pb.TagNumber(3) - void clearBuilder() => clearField(3); -} - -class BuilderMetadata extends $pb.GeneratedMessage { - factory BuilderMetadata() => create(); - BuilderMetadata._() : super(); - factory BuilderMetadata.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuilderMetadata.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuilderMetadata', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'owner') - ..aOM<$0.HealthStatus>(2, _omitFieldNames ? '' : 'health', subBuilder: $0.HealthStatus.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuilderMetadata clone() => BuilderMetadata()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuilderMetadata copyWith(void Function(BuilderMetadata) updates) => - super.copyWith((message) => updates(message as BuilderMetadata)) as BuilderMetadata; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuilderMetadata create() => BuilderMetadata._(); - BuilderMetadata createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuilderMetadata getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuilderMetadata? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get owner => $_getSZ(0); - @$pb.TagNumber(1) - set owner($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasOwner() => $_has(0); - @$pb.TagNumber(1) - void clearOwner() => clearField(1); - - @$pb.TagNumber(2) - $0.HealthStatus get health => $_getN(1); - @$pb.TagNumber(2) - set health($0.HealthStatus v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasHealth() => $_has(1); - @$pb.TagNumber(2) - void clearHealth() => clearField(2); - @$pb.TagNumber(2) - $0.HealthStatus ensureHealth() => $_ensure(1); -} - -class BuilderItem extends $pb.GeneratedMessage { - factory BuilderItem() => create(); - BuilderItem._() : super(); - factory BuilderItem.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuilderItem.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuilderItem', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOM(1, _omitFieldNames ? '' : 'id', subBuilder: BuilderID.create) - ..aOM<$1.BuilderConfig>(2, _omitFieldNames ? '' : 'config', subBuilder: $1.BuilderConfig.create) - ..aOM(3, _omitFieldNames ? '' : 'metadata', subBuilder: BuilderMetadata.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuilderItem clone() => BuilderItem()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuilderItem copyWith(void Function(BuilderItem) updates) => - super.copyWith((message) => updates(message as BuilderItem)) as BuilderItem; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuilderItem create() => BuilderItem._(); - BuilderItem createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuilderItem getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuilderItem? _defaultInstance; - - @$pb.TagNumber(1) - BuilderID get id => $_getN(0); - @$pb.TagNumber(1) - set id(BuilderID v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasId() => $_has(0); - @$pb.TagNumber(1) - void clearId() => clearField(1); - @$pb.TagNumber(1) - BuilderID ensureId() => $_ensure(0); - - @$pb.TagNumber(2) - $1.BuilderConfig get config => $_getN(1); - @$pb.TagNumber(2) - set config($1.BuilderConfig v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasConfig() => $_has(1); - @$pb.TagNumber(2) - void clearConfig() => clearField(2); - @$pb.TagNumber(2) - $1.BuilderConfig ensureConfig() => $_ensure(1); - - @$pb.TagNumber(3) - BuilderMetadata get metadata => $_getN(2); - @$pb.TagNumber(3) - set metadata(BuilderMetadata v) { - setField(3, v); - } - - @$pb.TagNumber(3) - $core.bool hasMetadata() => $_has(2); - @$pb.TagNumber(3) - void clearMetadata() => clearField(3); - @$pb.TagNumber(3) - BuilderMetadata ensureMetadata() => $_ensure(2); -} - -const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); -const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_common.pbenum.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_common.pbenum.dart deleted file mode 100644 index 083770066..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_common.pbenum.dart +++ /dev/null @@ -1,10 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/builder_common.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_common.pbjson.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_common.pbjson.dart deleted file mode 100644 index ddd390092..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_common.pbjson.dart +++ /dev/null @@ -1,61 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/builder_common.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:convert' as $convert; -import 'dart:core' as $core; -import 'dart:typed_data' as $typed_data; - -@$core.Deprecated('Use builderIDDescriptor instead') -const BuilderID$json = { - '1': 'BuilderID', - '2': [ - {'1': 'project', '3': 1, '4': 1, '5': 9, '8': {}, '10': 'project'}, - {'1': 'bucket', '3': 2, '4': 1, '5': 9, '8': {}, '10': 'bucket'}, - {'1': 'builder', '3': 3, '4': 1, '5': 9, '8': {}, '10': 'builder'}, - ], -}; - -/// Descriptor for `BuilderID`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List builderIDDescriptor = - $convert.base64Decode('CglCdWlsZGVySUQSLgoHcHJvamVjdBgBIAEoCUIUmsMaEFNldEJ1aWxkZXJIZWFsdGhSB3Byb2' - 'plY3QSLAoGYnVja2V0GAIgASgJQhSawxoQU2V0QnVpbGRlckhlYWx0aFIGYnVja2V0Ei4KB2J1' - 'aWxkZXIYAyABKAlCFJrDGhBTZXRCdWlsZGVySGVhbHRoUgdidWlsZGVy'); - -@$core.Deprecated('Use builderMetadataDescriptor instead') -const BuilderMetadata$json = { - '1': 'BuilderMetadata', - '2': [ - {'1': 'owner', '3': 1, '4': 1, '5': 9, '10': 'owner'}, - {'1': 'health', '3': 2, '4': 1, '5': 11, '6': '.buildbucket.v2.HealthStatus', '10': 'health'}, - ], -}; - -/// Descriptor for `BuilderMetadata`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List builderMetadataDescriptor = - $convert.base64Decode('Cg9CdWlsZGVyTWV0YWRhdGESFAoFb3duZXIYASABKAlSBW93bmVyEjQKBmhlYWx0aBgCIAEoCz' - 'IcLmJ1aWxkYnVja2V0LnYyLkhlYWx0aFN0YXR1c1IGaGVhbHRo'); - -@$core.Deprecated('Use builderItemDescriptor instead') -const BuilderItem$json = { - '1': 'BuilderItem', - '2': [ - {'1': 'id', '3': 1, '4': 1, '5': 11, '6': '.buildbucket.v2.BuilderID', '10': 'id'}, - {'1': 'config', '3': 2, '4': 1, '5': 11, '6': '.buildbucket.BuilderConfig', '10': 'config'}, - {'1': 'metadata', '3': 3, '4': 1, '5': 11, '6': '.buildbucket.v2.BuilderMetadata', '10': 'metadata'}, - ], -}; - -/// Descriptor for `BuilderItem`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List builderItemDescriptor = - $convert.base64Decode('CgtCdWlsZGVySXRlbRIpCgJpZBgBIAEoCzIZLmJ1aWxkYnVja2V0LnYyLkJ1aWxkZXJJRFICaW' - 'QSMgoGY29uZmlnGAIgASgLMhouYnVpbGRidWNrZXQuQnVpbGRlckNvbmZpZ1IGY29uZmlnEjsK' - 'CG1ldGFkYXRhGAMgASgLMh8uYnVpbGRidWNrZXQudjIuQnVpbGRlck1ldGFkYXRhUghtZXRhZG' - 'F0YQ=='); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_common.pbserver.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_common.pbserver.dart deleted file mode 100644 index 1993023a4..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_common.pbserver.dart +++ /dev/null @@ -1,8 +0,0 @@ -/// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/builder_common.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 'builder_common.pb.dart'; diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_service.pb.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_service.pb.dart deleted file mode 100644 index 7f73e2666..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_service.pb.dart +++ /dev/null @@ -1,507 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/builder_service.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:core' as $core; - -import 'package:protobuf/protobuf.dart' as $pb; - -import '../../../../google/protobuf/empty.pb.dart' as $3; -import '../../../../google/rpc/status.pb.dart' as $4; -import 'builder_common.pb.dart' as $1; -import 'builder_service.pbenum.dart'; -import 'common.pb.dart' as $2; - -export 'builder_service.pbenum.dart'; - -class GetBuilderRequest extends $pb.GeneratedMessage { - factory GetBuilderRequest() => create(); - GetBuilderRequest._() : super(); - factory GetBuilderRequest.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory GetBuilderRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'GetBuilderRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOM<$1.BuilderID>(1, _omitFieldNames ? '' : 'id', subBuilder: $1.BuilderID.create) - ..aOM(2, _omitFieldNames ? '' : 'mask', subBuilder: BuilderMask.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - GetBuilderRequest clone() => GetBuilderRequest()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - GetBuilderRequest copyWith(void Function(GetBuilderRequest) updates) => - super.copyWith((message) => updates(message as GetBuilderRequest)) as GetBuilderRequest; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static GetBuilderRequest create() => GetBuilderRequest._(); - GetBuilderRequest createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static GetBuilderRequest getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static GetBuilderRequest? _defaultInstance; - - @$pb.TagNumber(1) - $1.BuilderID get id => $_getN(0); - @$pb.TagNumber(1) - set id($1.BuilderID v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasId() => $_has(0); - @$pb.TagNumber(1) - void clearId() => clearField(1); - @$pb.TagNumber(1) - $1.BuilderID ensureId() => $_ensure(0); - - @$pb.TagNumber(2) - BuilderMask get mask => $_getN(1); - @$pb.TagNumber(2) - set mask(BuilderMask v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasMask() => $_has(1); - @$pb.TagNumber(2) - void clearMask() => clearField(2); - @$pb.TagNumber(2) - BuilderMask ensureMask() => $_ensure(1); -} - -class ListBuildersRequest extends $pb.GeneratedMessage { - factory ListBuildersRequest() => create(); - ListBuildersRequest._() : super(); - factory ListBuildersRequest.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory ListBuildersRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ListBuildersRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'project') - ..aOS(2, _omitFieldNames ? '' : 'bucket') - ..a<$core.int>(3, _omitFieldNames ? '' : 'pageSize', $pb.PbFieldType.O3) - ..aOS(4, _omitFieldNames ? '' : 'pageToken') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - ListBuildersRequest clone() => ListBuildersRequest()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - ListBuildersRequest copyWith(void Function(ListBuildersRequest) updates) => - super.copyWith((message) => updates(message as ListBuildersRequest)) as ListBuildersRequest; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static ListBuildersRequest create() => ListBuildersRequest._(); - ListBuildersRequest createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static ListBuildersRequest getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static ListBuildersRequest? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get project => $_getSZ(0); - @$pb.TagNumber(1) - set project($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasProject() => $_has(0); - @$pb.TagNumber(1) - void clearProject() => clearField(1); - - @$pb.TagNumber(2) - $core.String get bucket => $_getSZ(1); - @$pb.TagNumber(2) - set bucket($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasBucket() => $_has(1); - @$pb.TagNumber(2) - void clearBucket() => clearField(2); - - @$pb.TagNumber(3) - $core.int get pageSize => $_getIZ(2); - @$pb.TagNumber(3) - set pageSize($core.int v) { - $_setSignedInt32(2, v); - } - - @$pb.TagNumber(3) - $core.bool hasPageSize() => $_has(2); - @$pb.TagNumber(3) - void clearPageSize() => clearField(3); - - @$pb.TagNumber(4) - $core.String get pageToken => $_getSZ(3); - @$pb.TagNumber(4) - set pageToken($core.String v) { - $_setString(3, v); - } - - @$pb.TagNumber(4) - $core.bool hasPageToken() => $_has(3); - @$pb.TagNumber(4) - void clearPageToken() => clearField(4); -} - -class ListBuildersResponse extends $pb.GeneratedMessage { - factory ListBuildersResponse() => create(); - ListBuildersResponse._() : super(); - factory ListBuildersResponse.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory ListBuildersResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ListBuildersResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..pc<$1.BuilderItem>(1, _omitFieldNames ? '' : 'builders', $pb.PbFieldType.PM, subBuilder: $1.BuilderItem.create) - ..aOS(2, _omitFieldNames ? '' : 'nextPageToken') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - ListBuildersResponse clone() => ListBuildersResponse()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - ListBuildersResponse copyWith(void Function(ListBuildersResponse) updates) => - super.copyWith((message) => updates(message as ListBuildersResponse)) as ListBuildersResponse; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static ListBuildersResponse create() => ListBuildersResponse._(); - ListBuildersResponse createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static ListBuildersResponse getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static ListBuildersResponse? _defaultInstance; - - @$pb.TagNumber(1) - $core.List<$1.BuilderItem> get builders => $_getList(0); - - @$pb.TagNumber(2) - $core.String get nextPageToken => $_getSZ(1); - @$pb.TagNumber(2) - set nextPageToken($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasNextPageToken() => $_has(1); - @$pb.TagNumber(2) - void clearNextPageToken() => clearField(2); -} - -class SetBuilderHealthRequest_BuilderHealth extends $pb.GeneratedMessage { - factory SetBuilderHealthRequest_BuilderHealth() => create(); - SetBuilderHealthRequest_BuilderHealth._() : super(); - factory SetBuilderHealthRequest_BuilderHealth.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory SetBuilderHealthRequest_BuilderHealth.fromJson($core.String i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'SetBuilderHealthRequest.BuilderHealth', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOM<$1.BuilderID>(1, _omitFieldNames ? '' : 'id', subBuilder: $1.BuilderID.create) - ..aOM<$2.HealthStatus>(2, _omitFieldNames ? '' : 'health', subBuilder: $2.HealthStatus.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - SetBuilderHealthRequest_BuilderHealth clone() => SetBuilderHealthRequest_BuilderHealth()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - SetBuilderHealthRequest_BuilderHealth copyWith(void Function(SetBuilderHealthRequest_BuilderHealth) updates) => - super.copyWith((message) => updates(message as SetBuilderHealthRequest_BuilderHealth)) - as SetBuilderHealthRequest_BuilderHealth; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static SetBuilderHealthRequest_BuilderHealth create() => SetBuilderHealthRequest_BuilderHealth._(); - SetBuilderHealthRequest_BuilderHealth createEmptyInstance() => create(); - static $pb.PbList createRepeated() => - $pb.PbList(); - @$core.pragma('dart2js:noInline') - static SetBuilderHealthRequest_BuilderHealth getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static SetBuilderHealthRequest_BuilderHealth? _defaultInstance; - - @$pb.TagNumber(1) - $1.BuilderID get id => $_getN(0); - @$pb.TagNumber(1) - set id($1.BuilderID v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasId() => $_has(0); - @$pb.TagNumber(1) - void clearId() => clearField(1); - @$pb.TagNumber(1) - $1.BuilderID ensureId() => $_ensure(0); - - @$pb.TagNumber(2) - $2.HealthStatus get health => $_getN(1); - @$pb.TagNumber(2) - set health($2.HealthStatus v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasHealth() => $_has(1); - @$pb.TagNumber(2) - void clearHealth() => clearField(2); - @$pb.TagNumber(2) - $2.HealthStatus ensureHealth() => $_ensure(1); -} - -class SetBuilderHealthRequest extends $pb.GeneratedMessage { - factory SetBuilderHealthRequest() => create(); - SetBuilderHealthRequest._() : super(); - factory SetBuilderHealthRequest.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory SetBuilderHealthRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'SetBuilderHealthRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..pc(1, _omitFieldNames ? '' : 'health', $pb.PbFieldType.PM, - subBuilder: SetBuilderHealthRequest_BuilderHealth.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - SetBuilderHealthRequest clone() => SetBuilderHealthRequest()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - SetBuilderHealthRequest copyWith(void Function(SetBuilderHealthRequest) updates) => - super.copyWith((message) => updates(message as SetBuilderHealthRequest)) as SetBuilderHealthRequest; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static SetBuilderHealthRequest create() => SetBuilderHealthRequest._(); - SetBuilderHealthRequest createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static SetBuilderHealthRequest getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static SetBuilderHealthRequest? _defaultInstance; - - @$pb.TagNumber(1) - $core.List get health => $_getList(0); -} - -enum SetBuilderHealthResponse_Response_Response { result, error, notSet } - -class SetBuilderHealthResponse_Response extends $pb.GeneratedMessage { - factory SetBuilderHealthResponse_Response() => create(); - SetBuilderHealthResponse_Response._() : super(); - factory SetBuilderHealthResponse_Response.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory SetBuilderHealthResponse_Response.fromJson($core.String i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static const $core.Map<$core.int, SetBuilderHealthResponse_Response_Response> - _SetBuilderHealthResponse_Response_ResponseByTag = { - 1: SetBuilderHealthResponse_Response_Response.result, - 100: SetBuilderHealthResponse_Response_Response.error, - 0: SetBuilderHealthResponse_Response_Response.notSet - }; - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'SetBuilderHealthResponse.Response', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..oo(0, [1, 100]) - ..aOM<$3.Empty>(1, _omitFieldNames ? '' : 'result', subBuilder: $3.Empty.create) - ..aOM<$4.Status>(100, _omitFieldNames ? '' : 'error', subBuilder: $4.Status.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - SetBuilderHealthResponse_Response clone() => SetBuilderHealthResponse_Response()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - SetBuilderHealthResponse_Response copyWith(void Function(SetBuilderHealthResponse_Response) updates) => - super.copyWith((message) => updates(message as SetBuilderHealthResponse_Response)) - as SetBuilderHealthResponse_Response; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static SetBuilderHealthResponse_Response create() => SetBuilderHealthResponse_Response._(); - SetBuilderHealthResponse_Response createEmptyInstance() => create(); - static $pb.PbList createRepeated() => - $pb.PbList(); - @$core.pragma('dart2js:noInline') - static SetBuilderHealthResponse_Response getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static SetBuilderHealthResponse_Response? _defaultInstance; - - SetBuilderHealthResponse_Response_Response whichResponse() => - _SetBuilderHealthResponse_Response_ResponseByTag[$_whichOneof(0)]!; - void clearResponse() => clearField($_whichOneof(0)); - - @$pb.TagNumber(1) - $3.Empty get result => $_getN(0); - @$pb.TagNumber(1) - set result($3.Empty v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasResult() => $_has(0); - @$pb.TagNumber(1) - void clearResult() => clearField(1); - @$pb.TagNumber(1) - $3.Empty ensureResult() => $_ensure(0); - - @$pb.TagNumber(100) - $4.Status get error => $_getN(1); - @$pb.TagNumber(100) - set error($4.Status v) { - setField(100, v); - } - - @$pb.TagNumber(100) - $core.bool hasError() => $_has(1); - @$pb.TagNumber(100) - void clearError() => clearField(100); - @$pb.TagNumber(100) - $4.Status ensureError() => $_ensure(1); -} - -class SetBuilderHealthResponse extends $pb.GeneratedMessage { - factory SetBuilderHealthResponse() => create(); - SetBuilderHealthResponse._() : super(); - factory SetBuilderHealthResponse.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory SetBuilderHealthResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'SetBuilderHealthResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..pc(1, _omitFieldNames ? '' : 'responses', $pb.PbFieldType.PM, - subBuilder: SetBuilderHealthResponse_Response.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - SetBuilderHealthResponse clone() => SetBuilderHealthResponse()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - SetBuilderHealthResponse copyWith(void Function(SetBuilderHealthResponse) updates) => - super.copyWith((message) => updates(message as SetBuilderHealthResponse)) as SetBuilderHealthResponse; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static SetBuilderHealthResponse create() => SetBuilderHealthResponse._(); - SetBuilderHealthResponse createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static SetBuilderHealthResponse getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static SetBuilderHealthResponse? _defaultInstance; - - @$pb.TagNumber(1) - $core.List get responses => $_getList(0); -} - -class BuilderMask extends $pb.GeneratedMessage { - factory BuilderMask() => create(); - BuilderMask._() : super(); - factory BuilderMask.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuilderMask.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuilderMask', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..e(1, _omitFieldNames ? '' : 'type', $pb.PbFieldType.OE, - defaultOrMaker: BuilderMask_BuilderMaskType.BUILDER_MASK_TYPE_UNSPECIFIED, - valueOf: BuilderMask_BuilderMaskType.valueOf, - enumValues: BuilderMask_BuilderMaskType.values) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuilderMask clone() => BuilderMask()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuilderMask copyWith(void Function(BuilderMask) updates) => - super.copyWith((message) => updates(message as BuilderMask)) as BuilderMask; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuilderMask create() => BuilderMask._(); - BuilderMask createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuilderMask getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuilderMask? _defaultInstance; - - @$pb.TagNumber(1) - BuilderMask_BuilderMaskType get type => $_getN(0); - @$pb.TagNumber(1) - set type(BuilderMask_BuilderMaskType v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasType() => $_has(0); - @$pb.TagNumber(1) - void clearType() => clearField(1); -} - -const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); -const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_service.pbenum.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_service.pbenum.dart deleted file mode 100644 index d8e59b994..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_service.pbenum.dart +++ /dev/null @@ -1,38 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/builder_service.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:core' as $core; - -import 'package:protobuf/protobuf.dart' as $pb; - -class BuilderMask_BuilderMaskType extends $pb.ProtobufEnum { - static const BuilderMask_BuilderMaskType BUILDER_MASK_TYPE_UNSPECIFIED = - BuilderMask_BuilderMaskType._(0, _omitEnumNames ? '' : 'BUILDER_MASK_TYPE_UNSPECIFIED'); - static const BuilderMask_BuilderMaskType CONFIG_ONLY = - BuilderMask_BuilderMaskType._(1, _omitEnumNames ? '' : 'CONFIG_ONLY'); - static const BuilderMask_BuilderMaskType ALL = BuilderMask_BuilderMaskType._(2, _omitEnumNames ? '' : 'ALL'); - static const BuilderMask_BuilderMaskType METADATA_ONLY = - BuilderMask_BuilderMaskType._(3, _omitEnumNames ? '' : 'METADATA_ONLY'); - - static const $core.List values = [ - BUILDER_MASK_TYPE_UNSPECIFIED, - CONFIG_ONLY, - ALL, - METADATA_ONLY, - ]; - - static final $core.Map<$core.int, BuilderMask_BuilderMaskType> _byValue = $pb.ProtobufEnum.initByValue(values); - static BuilderMask_BuilderMaskType? valueOf($core.int value) => _byValue[value]; - - const BuilderMask_BuilderMaskType._($core.int v, $core.String n) : super(v, n); -} - -const _omitEnumNames = $core.bool.fromEnvironment('protobuf.omit_enum_names'); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_service.pbgrpc.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_service.pbgrpc.dart deleted file mode 100644 index a3a5a0e61..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_service.pbgrpc.dart +++ /dev/null @@ -1,104 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/builder_service.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:async' as $async; -import 'dart:core' as $core; - -import 'package:grpc/service_api.dart' as $grpc; -import 'package:protobuf/protobuf.dart' as $pb; - -import 'builder_common.pb.dart' as $1; -import 'builder_service.pb.dart' as $0; - -export 'builder_service.pb.dart'; - -@$pb.GrpcServiceName('buildbucket.v2.Builders') -class BuildersClient extends $grpc.Client { - static final _$getBuilder = $grpc.ClientMethod<$0.GetBuilderRequest, $1.BuilderItem>( - '/buildbucket.v2.Builders/GetBuilder', - ($0.GetBuilderRequest value) => value.writeToBuffer(), - ($core.List<$core.int> value) => $1.BuilderItem.fromBuffer(value)); - static final _$listBuilders = $grpc.ClientMethod<$0.ListBuildersRequest, $0.ListBuildersResponse>( - '/buildbucket.v2.Builders/ListBuilders', - ($0.ListBuildersRequest value) => value.writeToBuffer(), - ($core.List<$core.int> value) => $0.ListBuildersResponse.fromBuffer(value)); - static final _$setBuilderHealth = $grpc.ClientMethod<$0.SetBuilderHealthRequest, $0.SetBuilderHealthResponse>( - '/buildbucket.v2.Builders/SetBuilderHealth', - ($0.SetBuilderHealthRequest value) => value.writeToBuffer(), - ($core.List<$core.int> value) => $0.SetBuilderHealthResponse.fromBuffer(value)); - - BuildersClient($grpc.ClientChannel channel, - {$grpc.CallOptions? options, $core.Iterable<$grpc.ClientInterceptor>? interceptors}) - : super(channel, options: options, interceptors: interceptors); - - $grpc.ResponseFuture<$1.BuilderItem> getBuilder($0.GetBuilderRequest request, {$grpc.CallOptions? options}) { - return $createUnaryCall(_$getBuilder, request, options: options); - } - - $grpc.ResponseFuture<$0.ListBuildersResponse> listBuilders($0.ListBuildersRequest request, - {$grpc.CallOptions? options}) { - return $createUnaryCall(_$listBuilders, request, options: options); - } - - $grpc.ResponseFuture<$0.SetBuilderHealthResponse> setBuilderHealth($0.SetBuilderHealthRequest request, - {$grpc.CallOptions? options}) { - return $createUnaryCall(_$setBuilderHealth, request, options: options); - } -} - -@$pb.GrpcServiceName('buildbucket.v2.Builders') -abstract class BuildersServiceBase extends $grpc.Service { - $core.String get $name => 'buildbucket.v2.Builders'; - - BuildersServiceBase() { - $addMethod($grpc.ServiceMethod<$0.GetBuilderRequest, $1.BuilderItem>( - 'GetBuilder', - getBuilder_Pre, - false, - false, - ($core.List<$core.int> value) => $0.GetBuilderRequest.fromBuffer(value), - ($1.BuilderItem value) => value.writeToBuffer())); - $addMethod($grpc.ServiceMethod<$0.ListBuildersRequest, $0.ListBuildersResponse>( - 'ListBuilders', - listBuilders_Pre, - false, - false, - ($core.List<$core.int> value) => $0.ListBuildersRequest.fromBuffer(value), - ($0.ListBuildersResponse value) => value.writeToBuffer())); - $addMethod($grpc.ServiceMethod<$0.SetBuilderHealthRequest, $0.SetBuilderHealthResponse>( - 'SetBuilderHealth', - setBuilderHealth_Pre, - false, - false, - ($core.List<$core.int> value) => $0.SetBuilderHealthRequest.fromBuffer(value), - ($0.SetBuilderHealthResponse value) => value.writeToBuffer())); - } - - $async.Future<$1.BuilderItem> getBuilder_Pre( - $grpc.ServiceCall call, $async.Future<$0.GetBuilderRequest> request) async { - return getBuilder(call, await request); - } - - $async.Future<$0.ListBuildersResponse> listBuilders_Pre( - $grpc.ServiceCall call, $async.Future<$0.ListBuildersRequest> request) async { - return listBuilders(call, await request); - } - - $async.Future<$0.SetBuilderHealthResponse> setBuilderHealth_Pre( - $grpc.ServiceCall call, $async.Future<$0.SetBuilderHealthRequest> request) async { - return setBuilderHealth(call, await request); - } - - $async.Future<$1.BuilderItem> getBuilder($grpc.ServiceCall call, $0.GetBuilderRequest request); - $async.Future<$0.ListBuildersResponse> listBuilders($grpc.ServiceCall call, $0.ListBuildersRequest request); - $async.Future<$0.SetBuilderHealthResponse> setBuilderHealth( - $grpc.ServiceCall call, $0.SetBuilderHealthRequest request); -} diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_service.pbjson.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_service.pbjson.dart deleted file mode 100644 index 7cd0e0c6f..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builder_service.pbjson.dart +++ /dev/null @@ -1,156 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/builder_service.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:convert' as $convert; -import 'dart:core' as $core; -import 'dart:typed_data' as $typed_data; - -@$core.Deprecated('Use getBuilderRequestDescriptor instead') -const GetBuilderRequest$json = { - '1': 'GetBuilderRequest', - '2': [ - {'1': 'id', '3': 1, '4': 1, '5': 11, '6': '.buildbucket.v2.BuilderID', '10': 'id'}, - {'1': 'mask', '3': 2, '4': 1, '5': 11, '6': '.buildbucket.v2.BuilderMask', '10': 'mask'}, - ], -}; - -/// Descriptor for `GetBuilderRequest`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List getBuilderRequestDescriptor = - $convert.base64Decode('ChFHZXRCdWlsZGVyUmVxdWVzdBIpCgJpZBgBIAEoCzIZLmJ1aWxkYnVja2V0LnYyLkJ1aWxkZX' - 'JJRFICaWQSLwoEbWFzaxgCIAEoCzIbLmJ1aWxkYnVja2V0LnYyLkJ1aWxkZXJNYXNrUgRtYXNr'); - -@$core.Deprecated('Use listBuildersRequestDescriptor instead') -const ListBuildersRequest$json = { - '1': 'ListBuildersRequest', - '2': [ - {'1': 'project', '3': 1, '4': 1, '5': 9, '10': 'project'}, - {'1': 'bucket', '3': 2, '4': 1, '5': 9, '10': 'bucket'}, - {'1': 'page_size', '3': 3, '4': 1, '5': 5, '10': 'pageSize'}, - {'1': 'page_token', '3': 4, '4': 1, '5': 9, '10': 'pageToken'}, - ], -}; - -/// Descriptor for `ListBuildersRequest`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List listBuildersRequestDescriptor = - $convert.base64Decode('ChNMaXN0QnVpbGRlcnNSZXF1ZXN0EhgKB3Byb2plY3QYASABKAlSB3Byb2plY3QSFgoGYnVja2' - 'V0GAIgASgJUgZidWNrZXQSGwoJcGFnZV9zaXplGAMgASgFUghwYWdlU2l6ZRIdCgpwYWdlX3Rv' - 'a2VuGAQgASgJUglwYWdlVG9rZW4='); - -@$core.Deprecated('Use listBuildersResponseDescriptor instead') -const ListBuildersResponse$json = { - '1': 'ListBuildersResponse', - '2': [ - {'1': 'builders', '3': 1, '4': 3, '5': 11, '6': '.buildbucket.v2.BuilderItem', '10': 'builders'}, - {'1': 'next_page_token', '3': 2, '4': 1, '5': 9, '10': 'nextPageToken'}, - ], -}; - -/// Descriptor for `ListBuildersResponse`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List listBuildersResponseDescriptor = - $convert.base64Decode('ChRMaXN0QnVpbGRlcnNSZXNwb25zZRI3CghidWlsZGVycxgBIAMoCzIbLmJ1aWxkYnVja2V0Ln' - 'YyLkJ1aWxkZXJJdGVtUghidWlsZGVycxImCg9uZXh0X3BhZ2VfdG9rZW4YAiABKAlSDW5leHRQ' - 'YWdlVG9rZW4='); - -@$core.Deprecated('Use setBuilderHealthRequestDescriptor instead') -const SetBuilderHealthRequest$json = { - '1': 'SetBuilderHealthRequest', - '2': [ - { - '1': 'health', - '3': 1, - '4': 3, - '5': 11, - '6': '.buildbucket.v2.SetBuilderHealthRequest.BuilderHealth', - '10': 'health' - }, - ], - '3': [SetBuilderHealthRequest_BuilderHealth$json], -}; - -@$core.Deprecated('Use setBuilderHealthRequestDescriptor instead') -const SetBuilderHealthRequest_BuilderHealth$json = { - '1': 'BuilderHealth', - '2': [ - {'1': 'id', '3': 1, '4': 1, '5': 11, '6': '.buildbucket.v2.BuilderID', '8': {}, '10': 'id'}, - {'1': 'health', '3': 2, '4': 1, '5': 11, '6': '.buildbucket.v2.HealthStatus', '8': {}, '10': 'health'}, - ], -}; - -/// Descriptor for `SetBuilderHealthRequest`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List setBuilderHealthRequestDescriptor = - $convert.base64Decode('ChdTZXRCdWlsZGVySGVhbHRoUmVxdWVzdBJNCgZoZWFsdGgYASADKAsyNS5idWlsZGJ1Y2tldC' - '52Mi5TZXRCdWlsZGVySGVhbHRoUmVxdWVzdC5CdWlsZGVySGVhbHRoUgZoZWFsdGgaegoNQnVp' - 'bGRlckhlYWx0aBIuCgJpZBgBIAEoCzIZLmJ1aWxkYnVja2V0LnYyLkJ1aWxkZXJJREID4EECUg' - 'JpZBI5CgZoZWFsdGgYAiABKAsyHC5idWlsZGJ1Y2tldC52Mi5IZWFsdGhTdGF0dXNCA+BBAlIG' - 'aGVhbHRo'); - -@$core.Deprecated('Use setBuilderHealthResponseDescriptor instead') -const SetBuilderHealthResponse$json = { - '1': 'SetBuilderHealthResponse', - '2': [ - { - '1': 'responses', - '3': 1, - '4': 3, - '5': 11, - '6': '.buildbucket.v2.SetBuilderHealthResponse.Response', - '10': 'responses' - }, - ], - '3': [SetBuilderHealthResponse_Response$json], -}; - -@$core.Deprecated('Use setBuilderHealthResponseDescriptor instead') -const SetBuilderHealthResponse_Response$json = { - '1': 'Response', - '2': [ - {'1': 'result', '3': 1, '4': 1, '5': 11, '6': '.google.protobuf.Empty', '9': 0, '10': 'result'}, - {'1': 'error', '3': 100, '4': 1, '5': 11, '6': '.google.rpc.Status', '9': 0, '10': 'error'}, - ], - '8': [ - {'1': 'response'}, - ], -}; - -/// Descriptor for `SetBuilderHealthResponse`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List setBuilderHealthResponseDescriptor = - $convert.base64Decode('ChhTZXRCdWlsZGVySGVhbHRoUmVzcG9uc2USTwoJcmVzcG9uc2VzGAEgAygLMjEuYnVpbGRidW' - 'NrZXQudjIuU2V0QnVpbGRlckhlYWx0aFJlc3BvbnNlLlJlc3BvbnNlUglyZXNwb25zZXMadAoI' - 'UmVzcG9uc2USMAoGcmVzdWx0GAEgASgLMhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5SABSBnJlc3' - 'VsdBIqCgVlcnJvchhkIAEoCzISLmdvb2dsZS5ycGMuU3RhdHVzSABSBWVycm9yQgoKCHJlc3Bv' - 'bnNl'); - -@$core.Deprecated('Use builderMaskDescriptor instead') -const BuilderMask$json = { - '1': 'BuilderMask', - '2': [ - {'1': 'type', '3': 1, '4': 1, '5': 14, '6': '.buildbucket.v2.BuilderMask.BuilderMaskType', '10': 'type'}, - ], - '4': [BuilderMask_BuilderMaskType$json], -}; - -@$core.Deprecated('Use builderMaskDescriptor instead') -const BuilderMask_BuilderMaskType$json = { - '1': 'BuilderMaskType', - '2': [ - {'1': 'BUILDER_MASK_TYPE_UNSPECIFIED', '2': 0}, - {'1': 'CONFIG_ONLY', '2': 1}, - {'1': 'ALL', '2': 2}, - {'1': 'METADATA_ONLY', '2': 3}, - ], -}; - -/// Descriptor for `BuilderMask`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List builderMaskDescriptor = - $convert.base64Decode('CgtCdWlsZGVyTWFzaxI/CgR0eXBlGAEgASgOMisuYnVpbGRidWNrZXQudjIuQnVpbGRlck1hc2' - 'suQnVpbGRlck1hc2tUeXBlUgR0eXBlImEKD0J1aWxkZXJNYXNrVHlwZRIhCh1CVUlMREVSX01B' - 'U0tfVFlQRV9VTlNQRUNJRklFRBAAEg8KC0NPTkZJR19PTkxZEAESBwoDQUxMEAISEQoNTUVUQU' - 'RBVEFfT05MWRAD'); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builds_service.pb.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builds_service.pb.dart deleted file mode 100644 index 87102337a..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builds_service.pb.dart +++ /dev/null @@ -1,2129 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/builds_service.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:core' as $core; - -import 'package:fixnum/fixnum.dart' as $fixnum; -import 'package:protobuf/protobuf.dart' as $pb; - -import '../../../../google/protobuf/duration.pb.dart' as $8; -import '../../../../google/protobuf/field_mask.pb.dart' as $3; -import '../../../../google/protobuf/struct.pb.dart' as $5; -import '../../../../google/rpc/status.pb.dart' as $4; -import '../../common/proto/structmask/structmask.pb.dart' as $9; -import 'build.pb.dart' as $1; -import 'builder_common.pb.dart' as $2; -import 'common.pb.dart' as $6; -import 'common.pbenum.dart' as $6; -import 'launcher.pb.dart' as $11; -import 'notification.pb.dart' as $7; -import 'task.pb.dart' as $10; - -class GetBuildRequest extends $pb.GeneratedMessage { - factory GetBuildRequest() => create(); - GetBuildRequest._() : super(); - factory GetBuildRequest.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory GetBuildRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'GetBuildRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aInt64(1, _omitFieldNames ? '' : 'id') - ..aOM<$2.BuilderID>(2, _omitFieldNames ? '' : 'builder', subBuilder: $2.BuilderID.create) - ..a<$core.int>(3, _omitFieldNames ? '' : 'buildNumber', $pb.PbFieldType.O3) - ..aOM<$3.FieldMask>(100, _omitFieldNames ? '' : 'fields', subBuilder: $3.FieldMask.create) - ..aOM(101, _omitFieldNames ? '' : 'mask', subBuilder: BuildMask.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - GetBuildRequest clone() => GetBuildRequest()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - GetBuildRequest copyWith(void Function(GetBuildRequest) updates) => - super.copyWith((message) => updates(message as GetBuildRequest)) as GetBuildRequest; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static GetBuildRequest create() => GetBuildRequest._(); - GetBuildRequest createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static GetBuildRequest getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static GetBuildRequest? _defaultInstance; - - @$pb.TagNumber(1) - $fixnum.Int64 get id => $_getI64(0); - @$pb.TagNumber(1) - set id($fixnum.Int64 v) { - $_setInt64(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasId() => $_has(0); - @$pb.TagNumber(1) - void clearId() => clearField(1); - - @$pb.TagNumber(2) - $2.BuilderID get builder => $_getN(1); - @$pb.TagNumber(2) - set builder($2.BuilderID v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasBuilder() => $_has(1); - @$pb.TagNumber(2) - void clearBuilder() => clearField(2); - @$pb.TagNumber(2) - $2.BuilderID ensureBuilder() => $_ensure(1); - - @$pb.TagNumber(3) - $core.int get buildNumber => $_getIZ(2); - @$pb.TagNumber(3) - set buildNumber($core.int v) { - $_setSignedInt32(2, v); - } - - @$pb.TagNumber(3) - $core.bool hasBuildNumber() => $_has(2); - @$pb.TagNumber(3) - void clearBuildNumber() => clearField(3); - - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(100) - $3.FieldMask get fields => $_getN(3); - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(100) - set fields($3.FieldMask v) { - setField(100, v); - } - - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(100) - $core.bool hasFields() => $_has(3); - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(100) - void clearFields() => clearField(100); - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(100) - $3.FieldMask ensureFields() => $_ensure(3); - - @$pb.TagNumber(101) - BuildMask get mask => $_getN(4); - @$pb.TagNumber(101) - set mask(BuildMask v) { - setField(101, v); - } - - @$pb.TagNumber(101) - $core.bool hasMask() => $_has(4); - @$pb.TagNumber(101) - void clearMask() => clearField(101); - @$pb.TagNumber(101) - BuildMask ensureMask() => $_ensure(4); -} - -class SearchBuildsRequest extends $pb.GeneratedMessage { - factory SearchBuildsRequest() => create(); - SearchBuildsRequest._() : super(); - factory SearchBuildsRequest.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory SearchBuildsRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'SearchBuildsRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOM(1, _omitFieldNames ? '' : 'predicate', subBuilder: BuildPredicate.create) - ..aOM<$3.FieldMask>(100, _omitFieldNames ? '' : 'fields', subBuilder: $3.FieldMask.create) - ..a<$core.int>(101, _omitFieldNames ? '' : 'pageSize', $pb.PbFieldType.O3) - ..aOS(102, _omitFieldNames ? '' : 'pageToken') - ..aOM(103, _omitFieldNames ? '' : 'mask', subBuilder: BuildMask.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - SearchBuildsRequest clone() => SearchBuildsRequest()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - SearchBuildsRequest copyWith(void Function(SearchBuildsRequest) updates) => - super.copyWith((message) => updates(message as SearchBuildsRequest)) as SearchBuildsRequest; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static SearchBuildsRequest create() => SearchBuildsRequest._(); - SearchBuildsRequest createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static SearchBuildsRequest getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static SearchBuildsRequest? _defaultInstance; - - @$pb.TagNumber(1) - BuildPredicate get predicate => $_getN(0); - @$pb.TagNumber(1) - set predicate(BuildPredicate v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasPredicate() => $_has(0); - @$pb.TagNumber(1) - void clearPredicate() => clearField(1); - @$pb.TagNumber(1) - BuildPredicate ensurePredicate() => $_ensure(0); - - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(100) - $3.FieldMask get fields => $_getN(1); - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(100) - set fields($3.FieldMask v) { - setField(100, v); - } - - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(100) - $core.bool hasFields() => $_has(1); - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(100) - void clearFields() => clearField(100); - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(100) - $3.FieldMask ensureFields() => $_ensure(1); - - @$pb.TagNumber(101) - $core.int get pageSize => $_getIZ(2); - @$pb.TagNumber(101) - set pageSize($core.int v) { - $_setSignedInt32(2, v); - } - - @$pb.TagNumber(101) - $core.bool hasPageSize() => $_has(2); - @$pb.TagNumber(101) - void clearPageSize() => clearField(101); - - @$pb.TagNumber(102) - $core.String get pageToken => $_getSZ(3); - @$pb.TagNumber(102) - set pageToken($core.String v) { - $_setString(3, v); - } - - @$pb.TagNumber(102) - $core.bool hasPageToken() => $_has(3); - @$pb.TagNumber(102) - void clearPageToken() => clearField(102); - - @$pb.TagNumber(103) - BuildMask get mask => $_getN(4); - @$pb.TagNumber(103) - set mask(BuildMask v) { - setField(103, v); - } - - @$pb.TagNumber(103) - $core.bool hasMask() => $_has(4); - @$pb.TagNumber(103) - void clearMask() => clearField(103); - @$pb.TagNumber(103) - BuildMask ensureMask() => $_ensure(4); -} - -class SearchBuildsResponse extends $pb.GeneratedMessage { - factory SearchBuildsResponse() => create(); - SearchBuildsResponse._() : super(); - factory SearchBuildsResponse.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory SearchBuildsResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'SearchBuildsResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..pc<$1.Build>(1, _omitFieldNames ? '' : 'builds', $pb.PbFieldType.PM, subBuilder: $1.Build.create) - ..aOS(100, _omitFieldNames ? '' : 'nextPageToken') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - SearchBuildsResponse clone() => SearchBuildsResponse()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - SearchBuildsResponse copyWith(void Function(SearchBuildsResponse) updates) => - super.copyWith((message) => updates(message as SearchBuildsResponse)) as SearchBuildsResponse; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static SearchBuildsResponse create() => SearchBuildsResponse._(); - SearchBuildsResponse createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static SearchBuildsResponse getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static SearchBuildsResponse? _defaultInstance; - - @$pb.TagNumber(1) - $core.List<$1.Build> get builds => $_getList(0); - - @$pb.TagNumber(100) - $core.String get nextPageToken => $_getSZ(1); - @$pb.TagNumber(100) - set nextPageToken($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(100) - $core.bool hasNextPageToken() => $_has(1); - @$pb.TagNumber(100) - void clearNextPageToken() => clearField(100); -} - -enum BatchRequest_Request_Request { getBuild, searchBuilds, scheduleBuild, cancelBuild, getBuildStatus, notSet } - -class BatchRequest_Request extends $pb.GeneratedMessage { - factory BatchRequest_Request() => create(); - BatchRequest_Request._() : super(); - factory BatchRequest_Request.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BatchRequest_Request.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static const $core.Map<$core.int, BatchRequest_Request_Request> _BatchRequest_Request_RequestByTag = { - 1: BatchRequest_Request_Request.getBuild, - 2: BatchRequest_Request_Request.searchBuilds, - 3: BatchRequest_Request_Request.scheduleBuild, - 4: BatchRequest_Request_Request.cancelBuild, - 5: BatchRequest_Request_Request.getBuildStatus, - 0: BatchRequest_Request_Request.notSet - }; - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BatchRequest.Request', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..oo(0, [1, 2, 3, 4, 5]) - ..aOM(1, _omitFieldNames ? '' : 'getBuild', subBuilder: GetBuildRequest.create) - ..aOM(2, _omitFieldNames ? '' : 'searchBuilds', subBuilder: SearchBuildsRequest.create) - ..aOM(3, _omitFieldNames ? '' : 'scheduleBuild', subBuilder: ScheduleBuildRequest.create) - ..aOM(4, _omitFieldNames ? '' : 'cancelBuild', subBuilder: CancelBuildRequest.create) - ..aOM(5, _omitFieldNames ? '' : 'getBuildStatus', subBuilder: GetBuildStatusRequest.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BatchRequest_Request clone() => BatchRequest_Request()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BatchRequest_Request copyWith(void Function(BatchRequest_Request) updates) => - super.copyWith((message) => updates(message as BatchRequest_Request)) as BatchRequest_Request; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BatchRequest_Request create() => BatchRequest_Request._(); - BatchRequest_Request createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BatchRequest_Request getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BatchRequest_Request? _defaultInstance; - - BatchRequest_Request_Request whichRequest() => _BatchRequest_Request_RequestByTag[$_whichOneof(0)]!; - void clearRequest() => clearField($_whichOneof(0)); - - @$pb.TagNumber(1) - GetBuildRequest get getBuild => $_getN(0); - @$pb.TagNumber(1) - set getBuild(GetBuildRequest v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasGetBuild() => $_has(0); - @$pb.TagNumber(1) - void clearGetBuild() => clearField(1); - @$pb.TagNumber(1) - GetBuildRequest ensureGetBuild() => $_ensure(0); - - @$pb.TagNumber(2) - SearchBuildsRequest get searchBuilds => $_getN(1); - @$pb.TagNumber(2) - set searchBuilds(SearchBuildsRequest v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasSearchBuilds() => $_has(1); - @$pb.TagNumber(2) - void clearSearchBuilds() => clearField(2); - @$pb.TagNumber(2) - SearchBuildsRequest ensureSearchBuilds() => $_ensure(1); - - @$pb.TagNumber(3) - ScheduleBuildRequest get scheduleBuild => $_getN(2); - @$pb.TagNumber(3) - set scheduleBuild(ScheduleBuildRequest v) { - setField(3, v); - } - - @$pb.TagNumber(3) - $core.bool hasScheduleBuild() => $_has(2); - @$pb.TagNumber(3) - void clearScheduleBuild() => clearField(3); - @$pb.TagNumber(3) - ScheduleBuildRequest ensureScheduleBuild() => $_ensure(2); - - @$pb.TagNumber(4) - CancelBuildRequest get cancelBuild => $_getN(3); - @$pb.TagNumber(4) - set cancelBuild(CancelBuildRequest v) { - setField(4, v); - } - - @$pb.TagNumber(4) - $core.bool hasCancelBuild() => $_has(3); - @$pb.TagNumber(4) - void clearCancelBuild() => clearField(4); - @$pb.TagNumber(4) - CancelBuildRequest ensureCancelBuild() => $_ensure(3); - - @$pb.TagNumber(5) - GetBuildStatusRequest get getBuildStatus => $_getN(4); - @$pb.TagNumber(5) - set getBuildStatus(GetBuildStatusRequest v) { - setField(5, v); - } - - @$pb.TagNumber(5) - $core.bool hasGetBuildStatus() => $_has(4); - @$pb.TagNumber(5) - void clearGetBuildStatus() => clearField(5); - @$pb.TagNumber(5) - GetBuildStatusRequest ensureGetBuildStatus() => $_ensure(4); -} - -class BatchRequest extends $pb.GeneratedMessage { - factory BatchRequest() => create(); - BatchRequest._() : super(); - factory BatchRequest.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BatchRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BatchRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..pc(1, _omitFieldNames ? '' : 'requests', $pb.PbFieldType.PM, - subBuilder: BatchRequest_Request.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BatchRequest clone() => BatchRequest()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BatchRequest copyWith(void Function(BatchRequest) updates) => - super.copyWith((message) => updates(message as BatchRequest)) as BatchRequest; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BatchRequest create() => BatchRequest._(); - BatchRequest createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BatchRequest getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BatchRequest? _defaultInstance; - - @$pb.TagNumber(1) - $core.List get requests => $_getList(0); -} - -enum BatchResponse_Response_Response { - getBuild, - searchBuilds, - scheduleBuild, - cancelBuild, - getBuildStatus, - error, - notSet -} - -class BatchResponse_Response extends $pb.GeneratedMessage { - factory BatchResponse_Response() => create(); - BatchResponse_Response._() : super(); - factory BatchResponse_Response.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BatchResponse_Response.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static const $core.Map<$core.int, BatchResponse_Response_Response> _BatchResponse_Response_ResponseByTag = { - 1: BatchResponse_Response_Response.getBuild, - 2: BatchResponse_Response_Response.searchBuilds, - 3: BatchResponse_Response_Response.scheduleBuild, - 4: BatchResponse_Response_Response.cancelBuild, - 5: BatchResponse_Response_Response.getBuildStatus, - 100: BatchResponse_Response_Response.error, - 0: BatchResponse_Response_Response.notSet - }; - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BatchResponse.Response', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..oo(0, [1, 2, 3, 4, 5, 100]) - ..aOM<$1.Build>(1, _omitFieldNames ? '' : 'getBuild', subBuilder: $1.Build.create) - ..aOM(2, _omitFieldNames ? '' : 'searchBuilds', subBuilder: SearchBuildsResponse.create) - ..aOM<$1.Build>(3, _omitFieldNames ? '' : 'scheduleBuild', subBuilder: $1.Build.create) - ..aOM<$1.Build>(4, _omitFieldNames ? '' : 'cancelBuild', subBuilder: $1.Build.create) - ..aOM<$1.Build>(5, _omitFieldNames ? '' : 'getBuildStatus', subBuilder: $1.Build.create) - ..aOM<$4.Status>(100, _omitFieldNames ? '' : 'error', subBuilder: $4.Status.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BatchResponse_Response clone() => BatchResponse_Response()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BatchResponse_Response copyWith(void Function(BatchResponse_Response) updates) => - super.copyWith((message) => updates(message as BatchResponse_Response)) as BatchResponse_Response; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BatchResponse_Response create() => BatchResponse_Response._(); - BatchResponse_Response createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BatchResponse_Response getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BatchResponse_Response? _defaultInstance; - - BatchResponse_Response_Response whichResponse() => _BatchResponse_Response_ResponseByTag[$_whichOneof(0)]!; - void clearResponse() => clearField($_whichOneof(0)); - - @$pb.TagNumber(1) - $1.Build get getBuild => $_getN(0); - @$pb.TagNumber(1) - set getBuild($1.Build v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasGetBuild() => $_has(0); - @$pb.TagNumber(1) - void clearGetBuild() => clearField(1); - @$pb.TagNumber(1) - $1.Build ensureGetBuild() => $_ensure(0); - - @$pb.TagNumber(2) - SearchBuildsResponse get searchBuilds => $_getN(1); - @$pb.TagNumber(2) - set searchBuilds(SearchBuildsResponse v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasSearchBuilds() => $_has(1); - @$pb.TagNumber(2) - void clearSearchBuilds() => clearField(2); - @$pb.TagNumber(2) - SearchBuildsResponse ensureSearchBuilds() => $_ensure(1); - - @$pb.TagNumber(3) - $1.Build get scheduleBuild => $_getN(2); - @$pb.TagNumber(3) - set scheduleBuild($1.Build v) { - setField(3, v); - } - - @$pb.TagNumber(3) - $core.bool hasScheduleBuild() => $_has(2); - @$pb.TagNumber(3) - void clearScheduleBuild() => clearField(3); - @$pb.TagNumber(3) - $1.Build ensureScheduleBuild() => $_ensure(2); - - @$pb.TagNumber(4) - $1.Build get cancelBuild => $_getN(3); - @$pb.TagNumber(4) - set cancelBuild($1.Build v) { - setField(4, v); - } - - @$pb.TagNumber(4) - $core.bool hasCancelBuild() => $_has(3); - @$pb.TagNumber(4) - void clearCancelBuild() => clearField(4); - @$pb.TagNumber(4) - $1.Build ensureCancelBuild() => $_ensure(3); - - @$pb.TagNumber(5) - $1.Build get getBuildStatus => $_getN(4); - @$pb.TagNumber(5) - set getBuildStatus($1.Build v) { - setField(5, v); - } - - @$pb.TagNumber(5) - $core.bool hasGetBuildStatus() => $_has(4); - @$pb.TagNumber(5) - void clearGetBuildStatus() => clearField(5); - @$pb.TagNumber(5) - $1.Build ensureGetBuildStatus() => $_ensure(4); - - @$pb.TagNumber(100) - $4.Status get error => $_getN(5); - @$pb.TagNumber(100) - set error($4.Status v) { - setField(100, v); - } - - @$pb.TagNumber(100) - $core.bool hasError() => $_has(5); - @$pb.TagNumber(100) - void clearError() => clearField(100); - @$pb.TagNumber(100) - $4.Status ensureError() => $_ensure(5); -} - -class BatchResponse extends $pb.GeneratedMessage { - factory BatchResponse() => create(); - BatchResponse._() : super(); - factory BatchResponse.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BatchResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BatchResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..pc(1, _omitFieldNames ? '' : 'responses', $pb.PbFieldType.PM, - subBuilder: BatchResponse_Response.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BatchResponse clone() => BatchResponse()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BatchResponse copyWith(void Function(BatchResponse) updates) => - super.copyWith((message) => updates(message as BatchResponse)) as BatchResponse; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BatchResponse create() => BatchResponse._(); - BatchResponse createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BatchResponse getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BatchResponse? _defaultInstance; - - @$pb.TagNumber(1) - $core.List get responses => $_getList(0); -} - -class UpdateBuildRequest extends $pb.GeneratedMessage { - factory UpdateBuildRequest() => create(); - UpdateBuildRequest._() : super(); - factory UpdateBuildRequest.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory UpdateBuildRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'UpdateBuildRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOM<$1.Build>(1, _omitFieldNames ? '' : 'build', subBuilder: $1.Build.create) - ..aOM<$3.FieldMask>(2, _omitFieldNames ? '' : 'updateMask', subBuilder: $3.FieldMask.create) - ..aOM<$3.FieldMask>(100, _omitFieldNames ? '' : 'fields', subBuilder: $3.FieldMask.create) - ..aOM(101, _omitFieldNames ? '' : 'mask', subBuilder: BuildMask.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - UpdateBuildRequest clone() => UpdateBuildRequest()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - UpdateBuildRequest copyWith(void Function(UpdateBuildRequest) updates) => - super.copyWith((message) => updates(message as UpdateBuildRequest)) as UpdateBuildRequest; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static UpdateBuildRequest create() => UpdateBuildRequest._(); - UpdateBuildRequest createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static UpdateBuildRequest getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static UpdateBuildRequest? _defaultInstance; - - @$pb.TagNumber(1) - $1.Build get build => $_getN(0); - @$pb.TagNumber(1) - set build($1.Build v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasBuild() => $_has(0); - @$pb.TagNumber(1) - void clearBuild() => clearField(1); - @$pb.TagNumber(1) - $1.Build ensureBuild() => $_ensure(0); - - @$pb.TagNumber(2) - $3.FieldMask get updateMask => $_getN(1); - @$pb.TagNumber(2) - set updateMask($3.FieldMask v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasUpdateMask() => $_has(1); - @$pb.TagNumber(2) - void clearUpdateMask() => clearField(2); - @$pb.TagNumber(2) - $3.FieldMask ensureUpdateMask() => $_ensure(1); - - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(100) - $3.FieldMask get fields => $_getN(2); - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(100) - set fields($3.FieldMask v) { - setField(100, v); - } - - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(100) - $core.bool hasFields() => $_has(2); - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(100) - void clearFields() => clearField(100); - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(100) - $3.FieldMask ensureFields() => $_ensure(2); - - @$pb.TagNumber(101) - BuildMask get mask => $_getN(3); - @$pb.TagNumber(101) - set mask(BuildMask v) { - setField(101, v); - } - - @$pb.TagNumber(101) - $core.bool hasMask() => $_has(3); - @$pb.TagNumber(101) - void clearMask() => clearField(101); - @$pb.TagNumber(101) - BuildMask ensureMask() => $_ensure(3); -} - -class ScheduleBuildRequest_Swarming extends $pb.GeneratedMessage { - factory ScheduleBuildRequest_Swarming() => create(); - ScheduleBuildRequest_Swarming._() : super(); - factory ScheduleBuildRequest_Swarming.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory ScheduleBuildRequest_Swarming.fromJson($core.String i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ScheduleBuildRequest.Swarming', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'parentRunId') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - ScheduleBuildRequest_Swarming clone() => ScheduleBuildRequest_Swarming()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - ScheduleBuildRequest_Swarming copyWith(void Function(ScheduleBuildRequest_Swarming) updates) => - super.copyWith((message) => updates(message as ScheduleBuildRequest_Swarming)) as ScheduleBuildRequest_Swarming; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static ScheduleBuildRequest_Swarming create() => ScheduleBuildRequest_Swarming._(); - ScheduleBuildRequest_Swarming createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static ScheduleBuildRequest_Swarming getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static ScheduleBuildRequest_Swarming? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get parentRunId => $_getSZ(0); - @$pb.TagNumber(1) - set parentRunId($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasParentRunId() => $_has(0); - @$pb.TagNumber(1) - void clearParentRunId() => clearField(1); -} - -class ScheduleBuildRequest_ShadowInput extends $pb.GeneratedMessage { - factory ScheduleBuildRequest_ShadowInput() => create(); - ScheduleBuildRequest_ShadowInput._() : super(); - factory ScheduleBuildRequest_ShadowInput.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory ScheduleBuildRequest_ShadowInput.fromJson($core.String i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ScheduleBuildRequest.ShadowInput', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - ScheduleBuildRequest_ShadowInput clone() => ScheduleBuildRequest_ShadowInput()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - ScheduleBuildRequest_ShadowInput copyWith(void Function(ScheduleBuildRequest_ShadowInput) updates) => - super.copyWith((message) => updates(message as ScheduleBuildRequest_ShadowInput)) - as ScheduleBuildRequest_ShadowInput; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static ScheduleBuildRequest_ShadowInput create() => ScheduleBuildRequest_ShadowInput._(); - ScheduleBuildRequest_ShadowInput createEmptyInstance() => create(); - static $pb.PbList createRepeated() => - $pb.PbList(); - @$core.pragma('dart2js:noInline') - static ScheduleBuildRequest_ShadowInput getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static ScheduleBuildRequest_ShadowInput? _defaultInstance; -} - -class ScheduleBuildRequest extends $pb.GeneratedMessage { - factory ScheduleBuildRequest() => create(); - ScheduleBuildRequest._() : super(); - factory ScheduleBuildRequest.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory ScheduleBuildRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ScheduleBuildRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'requestId') - ..aInt64(2, _omitFieldNames ? '' : 'templateBuildId') - ..aOM<$2.BuilderID>(3, _omitFieldNames ? '' : 'builder', subBuilder: $2.BuilderID.create) - ..e<$6.Trinary>(4, _omitFieldNames ? '' : 'canary', $pb.PbFieldType.OE, - defaultOrMaker: $6.Trinary.UNSET, valueOf: $6.Trinary.valueOf, enumValues: $6.Trinary.values) - ..e<$6.Trinary>(5, _omitFieldNames ? '' : 'experimental', $pb.PbFieldType.OE, - defaultOrMaker: $6.Trinary.UNSET, valueOf: $6.Trinary.valueOf, enumValues: $6.Trinary.values) - ..aOM<$5.Struct>(6, _omitFieldNames ? '' : 'properties', subBuilder: $5.Struct.create) - ..aOM<$6.GitilesCommit>(7, _omitFieldNames ? '' : 'gitilesCommit', subBuilder: $6.GitilesCommit.create) - ..pc<$6.GerritChange>(8, _omitFieldNames ? '' : 'gerritChanges', $pb.PbFieldType.PM, - subBuilder: $6.GerritChange.create) - ..pc<$6.StringPair>(9, _omitFieldNames ? '' : 'tags', $pb.PbFieldType.PM, subBuilder: $6.StringPair.create) - ..pc<$6.RequestedDimension>(10, _omitFieldNames ? '' : 'dimensions', $pb.PbFieldType.PM, - subBuilder: $6.RequestedDimension.create) - ..a<$core.int>(11, _omitFieldNames ? '' : 'priority', $pb.PbFieldType.O3) - ..aOM<$7.NotificationConfig>(12, _omitFieldNames ? '' : 'notify', subBuilder: $7.NotificationConfig.create) - ..e<$6.Trinary>(13, _omitFieldNames ? '' : 'critical', $pb.PbFieldType.OE, - defaultOrMaker: $6.Trinary.UNSET, valueOf: $6.Trinary.valueOf, enumValues: $6.Trinary.values) - ..aOM<$6.Executable>(14, _omitFieldNames ? '' : 'exe', subBuilder: $6.Executable.create) - ..aOM(15, _omitFieldNames ? '' : 'swarming', - subBuilder: ScheduleBuildRequest_Swarming.create) - ..m<$core.String, $core.bool>(16, _omitFieldNames ? '' : 'experiments', - entryClassName: 'ScheduleBuildRequest.ExperimentsEntry', - keyFieldType: $pb.PbFieldType.OS, - valueFieldType: $pb.PbFieldType.OB, - packageName: const $pb.PackageName('buildbucket.v2')) - ..aOM<$8.Duration>(17, _omitFieldNames ? '' : 'schedulingTimeout', subBuilder: $8.Duration.create) - ..aOM<$8.Duration>(18, _omitFieldNames ? '' : 'executionTimeout', subBuilder: $8.Duration.create) - ..aOM<$8.Duration>(19, _omitFieldNames ? '' : 'gracePeriod', subBuilder: $8.Duration.create) - ..aOB(20, _omitFieldNames ? '' : 'dryRun') - ..e<$6.Trinary>(21, _omitFieldNames ? '' : 'canOutliveParent', $pb.PbFieldType.OE, - defaultOrMaker: $6.Trinary.UNSET, valueOf: $6.Trinary.valueOf, enumValues: $6.Trinary.values) - ..e<$6.Trinary>(22, _omitFieldNames ? '' : 'retriable', $pb.PbFieldType.OE, - defaultOrMaker: $6.Trinary.UNSET, valueOf: $6.Trinary.valueOf, enumValues: $6.Trinary.values) - ..aOM(23, _omitFieldNames ? '' : 'shadowInput', - subBuilder: ScheduleBuildRequest_ShadowInput.create) - ..aOM<$3.FieldMask>(100, _omitFieldNames ? '' : 'fields', subBuilder: $3.FieldMask.create) - ..aOM(101, _omitFieldNames ? '' : 'mask', subBuilder: BuildMask.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - ScheduleBuildRequest clone() => ScheduleBuildRequest()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - ScheduleBuildRequest copyWith(void Function(ScheduleBuildRequest) updates) => - super.copyWith((message) => updates(message as ScheduleBuildRequest)) as ScheduleBuildRequest; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static ScheduleBuildRequest create() => ScheduleBuildRequest._(); - ScheduleBuildRequest createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static ScheduleBuildRequest getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static ScheduleBuildRequest? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get requestId => $_getSZ(0); - @$pb.TagNumber(1) - set requestId($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasRequestId() => $_has(0); - @$pb.TagNumber(1) - void clearRequestId() => clearField(1); - - @$pb.TagNumber(2) - $fixnum.Int64 get templateBuildId => $_getI64(1); - @$pb.TagNumber(2) - set templateBuildId($fixnum.Int64 v) { - $_setInt64(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasTemplateBuildId() => $_has(1); - @$pb.TagNumber(2) - void clearTemplateBuildId() => clearField(2); - - @$pb.TagNumber(3) - $2.BuilderID get builder => $_getN(2); - @$pb.TagNumber(3) - set builder($2.BuilderID v) { - setField(3, v); - } - - @$pb.TagNumber(3) - $core.bool hasBuilder() => $_has(2); - @$pb.TagNumber(3) - void clearBuilder() => clearField(3); - @$pb.TagNumber(3) - $2.BuilderID ensureBuilder() => $_ensure(2); - - @$pb.TagNumber(4) - $6.Trinary get canary => $_getN(3); - @$pb.TagNumber(4) - set canary($6.Trinary v) { - setField(4, v); - } - - @$pb.TagNumber(4) - $core.bool hasCanary() => $_has(3); - @$pb.TagNumber(4) - void clearCanary() => clearField(4); - - @$pb.TagNumber(5) - $6.Trinary get experimental => $_getN(4); - @$pb.TagNumber(5) - set experimental($6.Trinary v) { - setField(5, v); - } - - @$pb.TagNumber(5) - $core.bool hasExperimental() => $_has(4); - @$pb.TagNumber(5) - void clearExperimental() => clearField(5); - - @$pb.TagNumber(6) - $5.Struct get properties => $_getN(5); - @$pb.TagNumber(6) - set properties($5.Struct v) { - setField(6, v); - } - - @$pb.TagNumber(6) - $core.bool hasProperties() => $_has(5); - @$pb.TagNumber(6) - void clearProperties() => clearField(6); - @$pb.TagNumber(6) - $5.Struct ensureProperties() => $_ensure(5); - - @$pb.TagNumber(7) - $6.GitilesCommit get gitilesCommit => $_getN(6); - @$pb.TagNumber(7) - set gitilesCommit($6.GitilesCommit v) { - setField(7, v); - } - - @$pb.TagNumber(7) - $core.bool hasGitilesCommit() => $_has(6); - @$pb.TagNumber(7) - void clearGitilesCommit() => clearField(7); - @$pb.TagNumber(7) - $6.GitilesCommit ensureGitilesCommit() => $_ensure(6); - - @$pb.TagNumber(8) - $core.List<$6.GerritChange> get gerritChanges => $_getList(7); - - @$pb.TagNumber(9) - $core.List<$6.StringPair> get tags => $_getList(8); - - @$pb.TagNumber(10) - $core.List<$6.RequestedDimension> get dimensions => $_getList(9); - - @$pb.TagNumber(11) - $core.int get priority => $_getIZ(10); - @$pb.TagNumber(11) - set priority($core.int v) { - $_setSignedInt32(10, v); - } - - @$pb.TagNumber(11) - $core.bool hasPriority() => $_has(10); - @$pb.TagNumber(11) - void clearPriority() => clearField(11); - - @$pb.TagNumber(12) - $7.NotificationConfig get notify => $_getN(11); - @$pb.TagNumber(12) - set notify($7.NotificationConfig v) { - setField(12, v); - } - - @$pb.TagNumber(12) - $core.bool hasNotify() => $_has(11); - @$pb.TagNumber(12) - void clearNotify() => clearField(12); - @$pb.TagNumber(12) - $7.NotificationConfig ensureNotify() => $_ensure(11); - - @$pb.TagNumber(13) - $6.Trinary get critical => $_getN(12); - @$pb.TagNumber(13) - set critical($6.Trinary v) { - setField(13, v); - } - - @$pb.TagNumber(13) - $core.bool hasCritical() => $_has(12); - @$pb.TagNumber(13) - void clearCritical() => clearField(13); - - @$pb.TagNumber(14) - $6.Executable get exe => $_getN(13); - @$pb.TagNumber(14) - set exe($6.Executable v) { - setField(14, v); - } - - @$pb.TagNumber(14) - $core.bool hasExe() => $_has(13); - @$pb.TagNumber(14) - void clearExe() => clearField(14); - @$pb.TagNumber(14) - $6.Executable ensureExe() => $_ensure(13); - - @$pb.TagNumber(15) - ScheduleBuildRequest_Swarming get swarming => $_getN(14); - @$pb.TagNumber(15) - set swarming(ScheduleBuildRequest_Swarming v) { - setField(15, v); - } - - @$pb.TagNumber(15) - $core.bool hasSwarming() => $_has(14); - @$pb.TagNumber(15) - void clearSwarming() => clearField(15); - @$pb.TagNumber(15) - ScheduleBuildRequest_Swarming ensureSwarming() => $_ensure(14); - - @$pb.TagNumber(16) - $core.Map<$core.String, $core.bool> get experiments => $_getMap(15); - - @$pb.TagNumber(17) - $8.Duration get schedulingTimeout => $_getN(16); - @$pb.TagNumber(17) - set schedulingTimeout($8.Duration v) { - setField(17, v); - } - - @$pb.TagNumber(17) - $core.bool hasSchedulingTimeout() => $_has(16); - @$pb.TagNumber(17) - void clearSchedulingTimeout() => clearField(17); - @$pb.TagNumber(17) - $8.Duration ensureSchedulingTimeout() => $_ensure(16); - - @$pb.TagNumber(18) - $8.Duration get executionTimeout => $_getN(17); - @$pb.TagNumber(18) - set executionTimeout($8.Duration v) { - setField(18, v); - } - - @$pb.TagNumber(18) - $core.bool hasExecutionTimeout() => $_has(17); - @$pb.TagNumber(18) - void clearExecutionTimeout() => clearField(18); - @$pb.TagNumber(18) - $8.Duration ensureExecutionTimeout() => $_ensure(17); - - @$pb.TagNumber(19) - $8.Duration get gracePeriod => $_getN(18); - @$pb.TagNumber(19) - set gracePeriod($8.Duration v) { - setField(19, v); - } - - @$pb.TagNumber(19) - $core.bool hasGracePeriod() => $_has(18); - @$pb.TagNumber(19) - void clearGracePeriod() => clearField(19); - @$pb.TagNumber(19) - $8.Duration ensureGracePeriod() => $_ensure(18); - - @$pb.TagNumber(20) - $core.bool get dryRun => $_getBF(19); - @$pb.TagNumber(20) - set dryRun($core.bool v) { - $_setBool(19, v); - } - - @$pb.TagNumber(20) - $core.bool hasDryRun() => $_has(19); - @$pb.TagNumber(20) - void clearDryRun() => clearField(20); - - @$pb.TagNumber(21) - $6.Trinary get canOutliveParent => $_getN(20); - @$pb.TagNumber(21) - set canOutliveParent($6.Trinary v) { - setField(21, v); - } - - @$pb.TagNumber(21) - $core.bool hasCanOutliveParent() => $_has(20); - @$pb.TagNumber(21) - void clearCanOutliveParent() => clearField(21); - - @$pb.TagNumber(22) - $6.Trinary get retriable => $_getN(21); - @$pb.TagNumber(22) - set retriable($6.Trinary v) { - setField(22, v); - } - - @$pb.TagNumber(22) - $core.bool hasRetriable() => $_has(21); - @$pb.TagNumber(22) - void clearRetriable() => clearField(22); - - @$pb.TagNumber(23) - ScheduleBuildRequest_ShadowInput get shadowInput => $_getN(22); - @$pb.TagNumber(23) - set shadowInput(ScheduleBuildRequest_ShadowInput v) { - setField(23, v); - } - - @$pb.TagNumber(23) - $core.bool hasShadowInput() => $_has(22); - @$pb.TagNumber(23) - void clearShadowInput() => clearField(23); - @$pb.TagNumber(23) - ScheduleBuildRequest_ShadowInput ensureShadowInput() => $_ensure(22); - - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(100) - $3.FieldMask get fields => $_getN(23); - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(100) - set fields($3.FieldMask v) { - setField(100, v); - } - - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(100) - $core.bool hasFields() => $_has(23); - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(100) - void clearFields() => clearField(100); - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(100) - $3.FieldMask ensureFields() => $_ensure(23); - - @$pb.TagNumber(101) - BuildMask get mask => $_getN(24); - @$pb.TagNumber(101) - set mask(BuildMask v) { - setField(101, v); - } - - @$pb.TagNumber(101) - $core.bool hasMask() => $_has(24); - @$pb.TagNumber(101) - void clearMask() => clearField(101); - @$pb.TagNumber(101) - BuildMask ensureMask() => $_ensure(24); -} - -class CancelBuildRequest extends $pb.GeneratedMessage { - factory CancelBuildRequest() => create(); - CancelBuildRequest._() : super(); - factory CancelBuildRequest.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory CancelBuildRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'CancelBuildRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aInt64(1, _omitFieldNames ? '' : 'id') - ..aOS(2, _omitFieldNames ? '' : 'summaryMarkdown') - ..aOM<$3.FieldMask>(100, _omitFieldNames ? '' : 'fields', subBuilder: $3.FieldMask.create) - ..aOM(101, _omitFieldNames ? '' : 'mask', subBuilder: BuildMask.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - CancelBuildRequest clone() => CancelBuildRequest()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - CancelBuildRequest copyWith(void Function(CancelBuildRequest) updates) => - super.copyWith((message) => updates(message as CancelBuildRequest)) as CancelBuildRequest; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static CancelBuildRequest create() => CancelBuildRequest._(); - CancelBuildRequest createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static CancelBuildRequest getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static CancelBuildRequest? _defaultInstance; - - @$pb.TagNumber(1) - $fixnum.Int64 get id => $_getI64(0); - @$pb.TagNumber(1) - set id($fixnum.Int64 v) { - $_setInt64(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasId() => $_has(0); - @$pb.TagNumber(1) - void clearId() => clearField(1); - - @$pb.TagNumber(2) - $core.String get summaryMarkdown => $_getSZ(1); - @$pb.TagNumber(2) - set summaryMarkdown($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasSummaryMarkdown() => $_has(1); - @$pb.TagNumber(2) - void clearSummaryMarkdown() => clearField(2); - - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(100) - $3.FieldMask get fields => $_getN(2); - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(100) - set fields($3.FieldMask v) { - setField(100, v); - } - - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(100) - $core.bool hasFields() => $_has(2); - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(100) - void clearFields() => clearField(100); - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(100) - $3.FieldMask ensureFields() => $_ensure(2); - - @$pb.TagNumber(101) - BuildMask get mask => $_getN(3); - @$pb.TagNumber(101) - set mask(BuildMask v) { - setField(101, v); - } - - @$pb.TagNumber(101) - $core.bool hasMask() => $_has(3); - @$pb.TagNumber(101) - void clearMask() => clearField(101); - @$pb.TagNumber(101) - BuildMask ensureMask() => $_ensure(3); -} - -class CreateBuildRequest extends $pb.GeneratedMessage { - factory CreateBuildRequest() => create(); - CreateBuildRequest._() : super(); - factory CreateBuildRequest.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory CreateBuildRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'CreateBuildRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOM<$1.Build>(1, _omitFieldNames ? '' : 'build', subBuilder: $1.Build.create) - ..aOS(2, _omitFieldNames ? '' : 'requestId') - ..aOM(3, _omitFieldNames ? '' : 'mask', subBuilder: BuildMask.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - CreateBuildRequest clone() => CreateBuildRequest()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - CreateBuildRequest copyWith(void Function(CreateBuildRequest) updates) => - super.copyWith((message) => updates(message as CreateBuildRequest)) as CreateBuildRequest; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static CreateBuildRequest create() => CreateBuildRequest._(); - CreateBuildRequest createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static CreateBuildRequest getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static CreateBuildRequest? _defaultInstance; - - @$pb.TagNumber(1) - $1.Build get build => $_getN(0); - @$pb.TagNumber(1) - set build($1.Build v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasBuild() => $_has(0); - @$pb.TagNumber(1) - void clearBuild() => clearField(1); - @$pb.TagNumber(1) - $1.Build ensureBuild() => $_ensure(0); - - @$pb.TagNumber(2) - $core.String get requestId => $_getSZ(1); - @$pb.TagNumber(2) - set requestId($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasRequestId() => $_has(1); - @$pb.TagNumber(2) - void clearRequestId() => clearField(2); - - @$pb.TagNumber(3) - BuildMask get mask => $_getN(2); - @$pb.TagNumber(3) - set mask(BuildMask v) { - setField(3, v); - } - - @$pb.TagNumber(3) - $core.bool hasMask() => $_has(2); - @$pb.TagNumber(3) - void clearMask() => clearField(3); - @$pb.TagNumber(3) - BuildMask ensureMask() => $_ensure(2); -} - -class SynthesizeBuildRequest extends $pb.GeneratedMessage { - factory SynthesizeBuildRequest() => create(); - SynthesizeBuildRequest._() : super(); - factory SynthesizeBuildRequest.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory SynthesizeBuildRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'SynthesizeBuildRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aInt64(1, _omitFieldNames ? '' : 'templateBuildId') - ..aOM<$2.BuilderID>(2, _omitFieldNames ? '' : 'builder', subBuilder: $2.BuilderID.create) - ..m<$core.String, $core.bool>(3, _omitFieldNames ? '' : 'experiments', - entryClassName: 'SynthesizeBuildRequest.ExperimentsEntry', - keyFieldType: $pb.PbFieldType.OS, - valueFieldType: $pb.PbFieldType.OB, - packageName: const $pb.PackageName('buildbucket.v2')) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - SynthesizeBuildRequest clone() => SynthesizeBuildRequest()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - SynthesizeBuildRequest copyWith(void Function(SynthesizeBuildRequest) updates) => - super.copyWith((message) => updates(message as SynthesizeBuildRequest)) as SynthesizeBuildRequest; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static SynthesizeBuildRequest create() => SynthesizeBuildRequest._(); - SynthesizeBuildRequest createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static SynthesizeBuildRequest getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static SynthesizeBuildRequest? _defaultInstance; - - @$pb.TagNumber(1) - $fixnum.Int64 get templateBuildId => $_getI64(0); - @$pb.TagNumber(1) - set templateBuildId($fixnum.Int64 v) { - $_setInt64(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasTemplateBuildId() => $_has(0); - @$pb.TagNumber(1) - void clearTemplateBuildId() => clearField(1); - - @$pb.TagNumber(2) - $2.BuilderID get builder => $_getN(1); - @$pb.TagNumber(2) - set builder($2.BuilderID v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasBuilder() => $_has(1); - @$pb.TagNumber(2) - void clearBuilder() => clearField(2); - @$pb.TagNumber(2) - $2.BuilderID ensureBuilder() => $_ensure(1); - - @$pb.TagNumber(3) - $core.Map<$core.String, $core.bool> get experiments => $_getMap(2); -} - -class StartBuildRequest extends $pb.GeneratedMessage { - factory StartBuildRequest() => create(); - StartBuildRequest._() : super(); - factory StartBuildRequest.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory StartBuildRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'StartBuildRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'requestId') - ..aInt64(2, _omitFieldNames ? '' : 'buildId') - ..aOS(3, _omitFieldNames ? '' : 'taskId') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - StartBuildRequest clone() => StartBuildRequest()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - StartBuildRequest copyWith(void Function(StartBuildRequest) updates) => - super.copyWith((message) => updates(message as StartBuildRequest)) as StartBuildRequest; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static StartBuildRequest create() => StartBuildRequest._(); - StartBuildRequest createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static StartBuildRequest getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static StartBuildRequest? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get requestId => $_getSZ(0); - @$pb.TagNumber(1) - set requestId($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasRequestId() => $_has(0); - @$pb.TagNumber(1) - void clearRequestId() => clearField(1); - - @$pb.TagNumber(2) - $fixnum.Int64 get buildId => $_getI64(1); - @$pb.TagNumber(2) - set buildId($fixnum.Int64 v) { - $_setInt64(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasBuildId() => $_has(1); - @$pb.TagNumber(2) - void clearBuildId() => clearField(2); - - @$pb.TagNumber(3) - $core.String get taskId => $_getSZ(2); - @$pb.TagNumber(3) - set taskId($core.String v) { - $_setString(2, v); - } - - @$pb.TagNumber(3) - $core.bool hasTaskId() => $_has(2); - @$pb.TagNumber(3) - void clearTaskId() => clearField(3); -} - -class StartBuildResponse extends $pb.GeneratedMessage { - factory StartBuildResponse() => create(); - StartBuildResponse._() : super(); - factory StartBuildResponse.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory StartBuildResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'StartBuildResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOM<$1.Build>(1, _omitFieldNames ? '' : 'build', subBuilder: $1.Build.create) - ..aOS(2, _omitFieldNames ? '' : 'updateBuildToken') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - StartBuildResponse clone() => StartBuildResponse()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - StartBuildResponse copyWith(void Function(StartBuildResponse) updates) => - super.copyWith((message) => updates(message as StartBuildResponse)) as StartBuildResponse; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static StartBuildResponse create() => StartBuildResponse._(); - StartBuildResponse createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static StartBuildResponse getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static StartBuildResponse? _defaultInstance; - - @$pb.TagNumber(1) - $1.Build get build => $_getN(0); - @$pb.TagNumber(1) - set build($1.Build v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasBuild() => $_has(0); - @$pb.TagNumber(1) - void clearBuild() => clearField(1); - @$pb.TagNumber(1) - $1.Build ensureBuild() => $_ensure(0); - - @$pb.TagNumber(2) - $core.String get updateBuildToken => $_getSZ(1); - @$pb.TagNumber(2) - set updateBuildToken($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasUpdateBuildToken() => $_has(1); - @$pb.TagNumber(2) - void clearUpdateBuildToken() => clearField(2); -} - -class GetBuildStatusRequest extends $pb.GeneratedMessage { - factory GetBuildStatusRequest() => create(); - GetBuildStatusRequest._() : super(); - factory GetBuildStatusRequest.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory GetBuildStatusRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'GetBuildStatusRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aInt64(1, _omitFieldNames ? '' : 'id') - ..aOM<$2.BuilderID>(2, _omitFieldNames ? '' : 'builder', subBuilder: $2.BuilderID.create) - ..a<$core.int>(3, _omitFieldNames ? '' : 'buildNumber', $pb.PbFieldType.O3) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - GetBuildStatusRequest clone() => GetBuildStatusRequest()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - GetBuildStatusRequest copyWith(void Function(GetBuildStatusRequest) updates) => - super.copyWith((message) => updates(message as GetBuildStatusRequest)) as GetBuildStatusRequest; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static GetBuildStatusRequest create() => GetBuildStatusRequest._(); - GetBuildStatusRequest createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static GetBuildStatusRequest getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static GetBuildStatusRequest? _defaultInstance; - - @$pb.TagNumber(1) - $fixnum.Int64 get id => $_getI64(0); - @$pb.TagNumber(1) - set id($fixnum.Int64 v) { - $_setInt64(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasId() => $_has(0); - @$pb.TagNumber(1) - void clearId() => clearField(1); - - @$pb.TagNumber(2) - $2.BuilderID get builder => $_getN(1); - @$pb.TagNumber(2) - set builder($2.BuilderID v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasBuilder() => $_has(1); - @$pb.TagNumber(2) - void clearBuilder() => clearField(2); - @$pb.TagNumber(2) - $2.BuilderID ensureBuilder() => $_ensure(1); - - @$pb.TagNumber(3) - $core.int get buildNumber => $_getIZ(2); - @$pb.TagNumber(3) - set buildNumber($core.int v) { - $_setSignedInt32(2, v); - } - - @$pb.TagNumber(3) - $core.bool hasBuildNumber() => $_has(2); - @$pb.TagNumber(3) - void clearBuildNumber() => clearField(3); -} - -class BuildMask extends $pb.GeneratedMessage { - factory BuildMask() => create(); - BuildMask._() : super(); - factory BuildMask.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuildMask.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuildMask', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOM<$3.FieldMask>(1, _omitFieldNames ? '' : 'fields', subBuilder: $3.FieldMask.create) - ..pc<$9.StructMask>(2, _omitFieldNames ? '' : 'inputProperties', $pb.PbFieldType.PM, - subBuilder: $9.StructMask.create) - ..pc<$9.StructMask>(3, _omitFieldNames ? '' : 'outputProperties', $pb.PbFieldType.PM, - subBuilder: $9.StructMask.create) - ..pc<$9.StructMask>(4, _omitFieldNames ? '' : 'requestedProperties', $pb.PbFieldType.PM, - subBuilder: $9.StructMask.create) - ..aOB(5, _omitFieldNames ? '' : 'allFields') - ..pc<$6.Status>(6, _omitFieldNames ? '' : 'stepStatus', $pb.PbFieldType.KE, - valueOf: $6.Status.valueOf, enumValues: $6.Status.values, defaultEnumValue: $6.Status.STATUS_UNSPECIFIED) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuildMask clone() => BuildMask()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuildMask copyWith(void Function(BuildMask) updates) => - super.copyWith((message) => updates(message as BuildMask)) as BuildMask; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuildMask create() => BuildMask._(); - BuildMask createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuildMask getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuildMask? _defaultInstance; - - @$pb.TagNumber(1) - $3.FieldMask get fields => $_getN(0); - @$pb.TagNumber(1) - set fields($3.FieldMask v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasFields() => $_has(0); - @$pb.TagNumber(1) - void clearFields() => clearField(1); - @$pb.TagNumber(1) - $3.FieldMask ensureFields() => $_ensure(0); - - @$pb.TagNumber(2) - $core.List<$9.StructMask> get inputProperties => $_getList(1); - - @$pb.TagNumber(3) - $core.List<$9.StructMask> get outputProperties => $_getList(2); - - @$pb.TagNumber(4) - $core.List<$9.StructMask> get requestedProperties => $_getList(3); - - @$pb.TagNumber(5) - $core.bool get allFields => $_getBF(4); - @$pb.TagNumber(5) - set allFields($core.bool v) { - $_setBool(4, v); - } - - @$pb.TagNumber(5) - $core.bool hasAllFields() => $_has(4); - @$pb.TagNumber(5) - void clearAllFields() => clearField(5); - - @$pb.TagNumber(6) - $core.List<$6.Status> get stepStatus => $_getList(5); -} - -class BuildPredicate extends $pb.GeneratedMessage { - factory BuildPredicate() => create(); - BuildPredicate._() : super(); - factory BuildPredicate.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuildPredicate.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuildPredicate', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOM<$2.BuilderID>(1, _omitFieldNames ? '' : 'builder', subBuilder: $2.BuilderID.create) - ..e<$6.Status>(2, _omitFieldNames ? '' : 'status', $pb.PbFieldType.OE, - defaultOrMaker: $6.Status.STATUS_UNSPECIFIED, valueOf: $6.Status.valueOf, enumValues: $6.Status.values) - ..pc<$6.GerritChange>(3, _omitFieldNames ? '' : 'gerritChanges', $pb.PbFieldType.PM, - subBuilder: $6.GerritChange.create) - ..aOM<$6.GitilesCommit>(4, _omitFieldNames ? '' : 'outputGitilesCommit', subBuilder: $6.GitilesCommit.create) - ..aOS(5, _omitFieldNames ? '' : 'createdBy') - ..pc<$6.StringPair>(6, _omitFieldNames ? '' : 'tags', $pb.PbFieldType.PM, subBuilder: $6.StringPair.create) - ..aOM<$6.TimeRange>(7, _omitFieldNames ? '' : 'createTime', subBuilder: $6.TimeRange.create) - ..aOB(8, _omitFieldNames ? '' : 'includeExperimental') - ..aOM(9, _omitFieldNames ? '' : 'build', subBuilder: BuildRange.create) - ..e<$6.Trinary>(10, _omitFieldNames ? '' : 'canary', $pb.PbFieldType.OE, - defaultOrMaker: $6.Trinary.UNSET, valueOf: $6.Trinary.valueOf, enumValues: $6.Trinary.values) - ..pPS(11, _omitFieldNames ? '' : 'experiments') - ..aInt64(12, _omitFieldNames ? '' : 'descendantOf') - ..aInt64(13, _omitFieldNames ? '' : 'childOf') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuildPredicate clone() => BuildPredicate()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuildPredicate copyWith(void Function(BuildPredicate) updates) => - super.copyWith((message) => updates(message as BuildPredicate)) as BuildPredicate; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuildPredicate create() => BuildPredicate._(); - BuildPredicate createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuildPredicate getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuildPredicate? _defaultInstance; - - @$pb.TagNumber(1) - $2.BuilderID get builder => $_getN(0); - @$pb.TagNumber(1) - set builder($2.BuilderID v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasBuilder() => $_has(0); - @$pb.TagNumber(1) - void clearBuilder() => clearField(1); - @$pb.TagNumber(1) - $2.BuilderID ensureBuilder() => $_ensure(0); - - @$pb.TagNumber(2) - $6.Status get status => $_getN(1); - @$pb.TagNumber(2) - set status($6.Status v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasStatus() => $_has(1); - @$pb.TagNumber(2) - void clearStatus() => clearField(2); - - @$pb.TagNumber(3) - $core.List<$6.GerritChange> get gerritChanges => $_getList(2); - - @$pb.TagNumber(4) - $6.GitilesCommit get outputGitilesCommit => $_getN(3); - @$pb.TagNumber(4) - set outputGitilesCommit($6.GitilesCommit v) { - setField(4, v); - } - - @$pb.TagNumber(4) - $core.bool hasOutputGitilesCommit() => $_has(3); - @$pb.TagNumber(4) - void clearOutputGitilesCommit() => clearField(4); - @$pb.TagNumber(4) - $6.GitilesCommit ensureOutputGitilesCommit() => $_ensure(3); - - @$pb.TagNumber(5) - $core.String get createdBy => $_getSZ(4); - @$pb.TagNumber(5) - set createdBy($core.String v) { - $_setString(4, v); - } - - @$pb.TagNumber(5) - $core.bool hasCreatedBy() => $_has(4); - @$pb.TagNumber(5) - void clearCreatedBy() => clearField(5); - - @$pb.TagNumber(6) - $core.List<$6.StringPair> get tags => $_getList(5); - - @$pb.TagNumber(7) - $6.TimeRange get createTime => $_getN(6); - @$pb.TagNumber(7) - set createTime($6.TimeRange v) { - setField(7, v); - } - - @$pb.TagNumber(7) - $core.bool hasCreateTime() => $_has(6); - @$pb.TagNumber(7) - void clearCreateTime() => clearField(7); - @$pb.TagNumber(7) - $6.TimeRange ensureCreateTime() => $_ensure(6); - - @$pb.TagNumber(8) - $core.bool get includeExperimental => $_getBF(7); - @$pb.TagNumber(8) - set includeExperimental($core.bool v) { - $_setBool(7, v); - } - - @$pb.TagNumber(8) - $core.bool hasIncludeExperimental() => $_has(7); - @$pb.TagNumber(8) - void clearIncludeExperimental() => clearField(8); - - @$pb.TagNumber(9) - BuildRange get build => $_getN(8); - @$pb.TagNumber(9) - set build(BuildRange v) { - setField(9, v); - } - - @$pb.TagNumber(9) - $core.bool hasBuild() => $_has(8); - @$pb.TagNumber(9) - void clearBuild() => clearField(9); - @$pb.TagNumber(9) - BuildRange ensureBuild() => $_ensure(8); - - @$pb.TagNumber(10) - $6.Trinary get canary => $_getN(9); - @$pb.TagNumber(10) - set canary($6.Trinary v) { - setField(10, v); - } - - @$pb.TagNumber(10) - $core.bool hasCanary() => $_has(9); - @$pb.TagNumber(10) - void clearCanary() => clearField(10); - - @$pb.TagNumber(11) - $core.List<$core.String> get experiments => $_getList(10); - - @$pb.TagNumber(12) - $fixnum.Int64 get descendantOf => $_getI64(11); - @$pb.TagNumber(12) - set descendantOf($fixnum.Int64 v) { - $_setInt64(11, v); - } - - @$pb.TagNumber(12) - $core.bool hasDescendantOf() => $_has(11); - @$pb.TagNumber(12) - void clearDescendantOf() => clearField(12); - - @$pb.TagNumber(13) - $fixnum.Int64 get childOf => $_getI64(12); - @$pb.TagNumber(13) - set childOf($fixnum.Int64 v) { - $_setInt64(12, v); - } - - @$pb.TagNumber(13) - $core.bool hasChildOf() => $_has(12); - @$pb.TagNumber(13) - void clearChildOf() => clearField(13); -} - -class BuildRange extends $pb.GeneratedMessage { - factory BuildRange() => create(); - BuildRange._() : super(); - factory BuildRange.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuildRange.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuildRange', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aInt64(1, _omitFieldNames ? '' : 'startBuildId') - ..aInt64(2, _omitFieldNames ? '' : 'endBuildId') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuildRange clone() => BuildRange()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuildRange copyWith(void Function(BuildRange) updates) => - super.copyWith((message) => updates(message as BuildRange)) as BuildRange; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuildRange create() => BuildRange._(); - BuildRange createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuildRange getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuildRange? _defaultInstance; - - @$pb.TagNumber(1) - $fixnum.Int64 get startBuildId => $_getI64(0); - @$pb.TagNumber(1) - set startBuildId($fixnum.Int64 v) { - $_setInt64(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasStartBuildId() => $_has(0); - @$pb.TagNumber(1) - void clearStartBuildId() => clearField(1); - - @$pb.TagNumber(2) - $fixnum.Int64 get endBuildId => $_getI64(1); - @$pb.TagNumber(2) - set endBuildId($fixnum.Int64 v) { - $_setInt64(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasEndBuildId() => $_has(1); - @$pb.TagNumber(2) - void clearEndBuildId() => clearField(2); -} - -class StartBuildTaskRequest extends $pb.GeneratedMessage { - factory StartBuildTaskRequest() => create(); - StartBuildTaskRequest._() : super(); - factory StartBuildTaskRequest.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory StartBuildTaskRequest.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'StartBuildTaskRequest', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'requestId') - ..aInt64(2, _omitFieldNames ? '' : 'buildId') - ..aOM<$10.Task>(3, _omitFieldNames ? '' : 'task', subBuilder: $10.Task.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - StartBuildTaskRequest clone() => StartBuildTaskRequest()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - StartBuildTaskRequest copyWith(void Function(StartBuildTaskRequest) updates) => - super.copyWith((message) => updates(message as StartBuildTaskRequest)) as StartBuildTaskRequest; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static StartBuildTaskRequest create() => StartBuildTaskRequest._(); - StartBuildTaskRequest createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static StartBuildTaskRequest getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static StartBuildTaskRequest? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get requestId => $_getSZ(0); - @$pb.TagNumber(1) - set requestId($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasRequestId() => $_has(0); - @$pb.TagNumber(1) - void clearRequestId() => clearField(1); - - @$pb.TagNumber(2) - $fixnum.Int64 get buildId => $_getI64(1); - @$pb.TagNumber(2) - set buildId($fixnum.Int64 v) { - $_setInt64(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasBuildId() => $_has(1); - @$pb.TagNumber(2) - void clearBuildId() => clearField(2); - - @$pb.TagNumber(3) - $10.Task get task => $_getN(2); - @$pb.TagNumber(3) - set task($10.Task v) { - setField(3, v); - } - - @$pb.TagNumber(3) - $core.bool hasTask() => $_has(2); - @$pb.TagNumber(3) - void clearTask() => clearField(3); - @$pb.TagNumber(3) - $10.Task ensureTask() => $_ensure(2); -} - -class StartBuildTaskResponse extends $pb.GeneratedMessage { - factory StartBuildTaskResponse() => create(); - StartBuildTaskResponse._() : super(); - factory StartBuildTaskResponse.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory StartBuildTaskResponse.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'StartBuildTaskResponse', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOM<$11.BuildSecrets>(1, _omitFieldNames ? '' : 'secrets', subBuilder: $11.BuildSecrets.create) - ..aOS(2, _omitFieldNames ? '' : 'pubsubTopic') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - StartBuildTaskResponse clone() => StartBuildTaskResponse()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - StartBuildTaskResponse copyWith(void Function(StartBuildTaskResponse) updates) => - super.copyWith((message) => updates(message as StartBuildTaskResponse)) as StartBuildTaskResponse; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static StartBuildTaskResponse create() => StartBuildTaskResponse._(); - StartBuildTaskResponse createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static StartBuildTaskResponse getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static StartBuildTaskResponse? _defaultInstance; - - @$pb.TagNumber(1) - $11.BuildSecrets get secrets => $_getN(0); - @$pb.TagNumber(1) - set secrets($11.BuildSecrets v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasSecrets() => $_has(0); - @$pb.TagNumber(1) - void clearSecrets() => clearField(1); - @$pb.TagNumber(1) - $11.BuildSecrets ensureSecrets() => $_ensure(0); - - @$pb.TagNumber(2) - $core.String get pubsubTopic => $_getSZ(1); - @$pb.TagNumber(2) - set pubsubTopic($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasPubsubTopic() => $_has(1); - @$pb.TagNumber(2) - void clearPubsubTopic() => clearField(2); -} - -const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); -const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builds_service.pbenum.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builds_service.pbenum.dart deleted file mode 100644 index 64a196afb..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builds_service.pbenum.dart +++ /dev/null @@ -1,10 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/builds_service.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builds_service.pbgrpc.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builds_service.pbgrpc.dart deleted file mode 100644 index fe8bc6a75..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builds_service.pbgrpc.dart +++ /dev/null @@ -1,264 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/builds_service.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:async' as $async; -import 'dart:core' as $core; - -import 'package:grpc/service_api.dart' as $grpc; -import 'package:protobuf/protobuf.dart' as $pb; - -import 'build.pb.dart' as $1; -import 'builds_service.pb.dart' as $0; - -export 'builds_service.pb.dart'; - -@$pb.GrpcServiceName('buildbucket.v2.Builds') -class BuildsClient extends $grpc.Client { - static final _$getBuild = $grpc.ClientMethod<$0.GetBuildRequest, $1.Build>('/buildbucket.v2.Builds/GetBuild', - ($0.GetBuildRequest value) => value.writeToBuffer(), ($core.List<$core.int> value) => $1.Build.fromBuffer(value)); - static final _$searchBuilds = $grpc.ClientMethod<$0.SearchBuildsRequest, $0.SearchBuildsResponse>( - '/buildbucket.v2.Builds/SearchBuilds', - ($0.SearchBuildsRequest value) => value.writeToBuffer(), - ($core.List<$core.int> value) => $0.SearchBuildsResponse.fromBuffer(value)); - static final _$updateBuild = $grpc.ClientMethod<$0.UpdateBuildRequest, $1.Build>( - '/buildbucket.v2.Builds/UpdateBuild', - ($0.UpdateBuildRequest value) => value.writeToBuffer(), - ($core.List<$core.int> value) => $1.Build.fromBuffer(value)); - static final _$scheduleBuild = $grpc.ClientMethod<$0.ScheduleBuildRequest, $1.Build>( - '/buildbucket.v2.Builds/ScheduleBuild', - ($0.ScheduleBuildRequest value) => value.writeToBuffer(), - ($core.List<$core.int> value) => $1.Build.fromBuffer(value)); - static final _$cancelBuild = $grpc.ClientMethod<$0.CancelBuildRequest, $1.Build>( - '/buildbucket.v2.Builds/CancelBuild', - ($0.CancelBuildRequest value) => value.writeToBuffer(), - ($core.List<$core.int> value) => $1.Build.fromBuffer(value)); - static final _$batch = $grpc.ClientMethod<$0.BatchRequest, $0.BatchResponse>( - '/buildbucket.v2.Builds/Batch', - ($0.BatchRequest value) => value.writeToBuffer(), - ($core.List<$core.int> value) => $0.BatchResponse.fromBuffer(value)); - static final _$createBuild = $grpc.ClientMethod<$0.CreateBuildRequest, $1.Build>( - '/buildbucket.v2.Builds/CreateBuild', - ($0.CreateBuildRequest value) => value.writeToBuffer(), - ($core.List<$core.int> value) => $1.Build.fromBuffer(value)); - static final _$synthesizeBuild = $grpc.ClientMethod<$0.SynthesizeBuildRequest, $1.Build>( - '/buildbucket.v2.Builds/SynthesizeBuild', - ($0.SynthesizeBuildRequest value) => value.writeToBuffer(), - ($core.List<$core.int> value) => $1.Build.fromBuffer(value)); - static final _$getBuildStatus = $grpc.ClientMethod<$0.GetBuildStatusRequest, $1.Build>( - '/buildbucket.v2.Builds/GetBuildStatus', - ($0.GetBuildStatusRequest value) => value.writeToBuffer(), - ($core.List<$core.int> value) => $1.Build.fromBuffer(value)); - static final _$startBuild = $grpc.ClientMethod<$0.StartBuildRequest, $0.StartBuildResponse>( - '/buildbucket.v2.Builds/StartBuild', - ($0.StartBuildRequest value) => value.writeToBuffer(), - ($core.List<$core.int> value) => $0.StartBuildResponse.fromBuffer(value)); - static final _$startBuildTask = $grpc.ClientMethod<$0.StartBuildTaskRequest, $0.StartBuildTaskResponse>( - '/buildbucket.v2.Builds/StartBuildTask', - ($0.StartBuildTaskRequest value) => value.writeToBuffer(), - ($core.List<$core.int> value) => $0.StartBuildTaskResponse.fromBuffer(value)); - - BuildsClient($grpc.ClientChannel channel, - {$grpc.CallOptions? options, $core.Iterable<$grpc.ClientInterceptor>? interceptors}) - : super(channel, options: options, interceptors: interceptors); - - $grpc.ResponseFuture<$1.Build> getBuild($0.GetBuildRequest request, {$grpc.CallOptions? options}) { - return $createUnaryCall(_$getBuild, request, options: options); - } - - $grpc.ResponseFuture<$0.SearchBuildsResponse> searchBuilds($0.SearchBuildsRequest request, - {$grpc.CallOptions? options}) { - return $createUnaryCall(_$searchBuilds, request, options: options); - } - - $grpc.ResponseFuture<$1.Build> updateBuild($0.UpdateBuildRequest request, {$grpc.CallOptions? options}) { - return $createUnaryCall(_$updateBuild, request, options: options); - } - - $grpc.ResponseFuture<$1.Build> scheduleBuild($0.ScheduleBuildRequest request, {$grpc.CallOptions? options}) { - return $createUnaryCall(_$scheduleBuild, request, options: options); - } - - $grpc.ResponseFuture<$1.Build> cancelBuild($0.CancelBuildRequest request, {$grpc.CallOptions? options}) { - return $createUnaryCall(_$cancelBuild, request, options: options); - } - - $grpc.ResponseFuture<$0.BatchResponse> batch($0.BatchRequest request, {$grpc.CallOptions? options}) { - return $createUnaryCall(_$batch, request, options: options); - } - - $grpc.ResponseFuture<$1.Build> createBuild($0.CreateBuildRequest request, {$grpc.CallOptions? options}) { - return $createUnaryCall(_$createBuild, request, options: options); - } - - $grpc.ResponseFuture<$1.Build> synthesizeBuild($0.SynthesizeBuildRequest request, {$grpc.CallOptions? options}) { - return $createUnaryCall(_$synthesizeBuild, request, options: options); - } - - $grpc.ResponseFuture<$1.Build> getBuildStatus($0.GetBuildStatusRequest request, {$grpc.CallOptions? options}) { - return $createUnaryCall(_$getBuildStatus, request, options: options); - } - - $grpc.ResponseFuture<$0.StartBuildResponse> startBuild($0.StartBuildRequest request, {$grpc.CallOptions? options}) { - return $createUnaryCall(_$startBuild, request, options: options); - } - - $grpc.ResponseFuture<$0.StartBuildTaskResponse> startBuildTask($0.StartBuildTaskRequest request, - {$grpc.CallOptions? options}) { - return $createUnaryCall(_$startBuildTask, request, options: options); - } -} - -@$pb.GrpcServiceName('buildbucket.v2.Builds') -abstract class BuildsServiceBase extends $grpc.Service { - $core.String get $name => 'buildbucket.v2.Builds'; - - BuildsServiceBase() { - $addMethod($grpc.ServiceMethod<$0.GetBuildRequest, $1.Build>( - 'GetBuild', - getBuild_Pre, - false, - false, - ($core.List<$core.int> value) => $0.GetBuildRequest.fromBuffer(value), - ($1.Build value) => value.writeToBuffer())); - $addMethod($grpc.ServiceMethod<$0.SearchBuildsRequest, $0.SearchBuildsResponse>( - 'SearchBuilds', - searchBuilds_Pre, - false, - false, - ($core.List<$core.int> value) => $0.SearchBuildsRequest.fromBuffer(value), - ($0.SearchBuildsResponse value) => value.writeToBuffer())); - $addMethod($grpc.ServiceMethod<$0.UpdateBuildRequest, $1.Build>( - 'UpdateBuild', - updateBuild_Pre, - false, - false, - ($core.List<$core.int> value) => $0.UpdateBuildRequest.fromBuffer(value), - ($1.Build value) => value.writeToBuffer())); - $addMethod($grpc.ServiceMethod<$0.ScheduleBuildRequest, $1.Build>( - 'ScheduleBuild', - scheduleBuild_Pre, - false, - false, - ($core.List<$core.int> value) => $0.ScheduleBuildRequest.fromBuffer(value), - ($1.Build value) => value.writeToBuffer())); - $addMethod($grpc.ServiceMethod<$0.CancelBuildRequest, $1.Build>( - 'CancelBuild', - cancelBuild_Pre, - false, - false, - ($core.List<$core.int> value) => $0.CancelBuildRequest.fromBuffer(value), - ($1.Build value) => value.writeToBuffer())); - $addMethod($grpc.ServiceMethod<$0.BatchRequest, $0.BatchResponse>( - 'Batch', - batch_Pre, - false, - false, - ($core.List<$core.int> value) => $0.BatchRequest.fromBuffer(value), - ($0.BatchResponse value) => value.writeToBuffer())); - $addMethod($grpc.ServiceMethod<$0.CreateBuildRequest, $1.Build>( - 'CreateBuild', - createBuild_Pre, - false, - false, - ($core.List<$core.int> value) => $0.CreateBuildRequest.fromBuffer(value), - ($1.Build value) => value.writeToBuffer())); - $addMethod($grpc.ServiceMethod<$0.SynthesizeBuildRequest, $1.Build>( - 'SynthesizeBuild', - synthesizeBuild_Pre, - false, - false, - ($core.List<$core.int> value) => $0.SynthesizeBuildRequest.fromBuffer(value), - ($1.Build value) => value.writeToBuffer())); - $addMethod($grpc.ServiceMethod<$0.GetBuildStatusRequest, $1.Build>( - 'GetBuildStatus', - getBuildStatus_Pre, - false, - false, - ($core.List<$core.int> value) => $0.GetBuildStatusRequest.fromBuffer(value), - ($1.Build value) => value.writeToBuffer())); - $addMethod($grpc.ServiceMethod<$0.StartBuildRequest, $0.StartBuildResponse>( - 'StartBuild', - startBuild_Pre, - false, - false, - ($core.List<$core.int> value) => $0.StartBuildRequest.fromBuffer(value), - ($0.StartBuildResponse value) => value.writeToBuffer())); - $addMethod($grpc.ServiceMethod<$0.StartBuildTaskRequest, $0.StartBuildTaskResponse>( - 'StartBuildTask', - startBuildTask_Pre, - false, - false, - ($core.List<$core.int> value) => $0.StartBuildTaskRequest.fromBuffer(value), - ($0.StartBuildTaskResponse value) => value.writeToBuffer())); - } - - $async.Future<$1.Build> getBuild_Pre($grpc.ServiceCall call, $async.Future<$0.GetBuildRequest> request) async { - return getBuild(call, await request); - } - - $async.Future<$0.SearchBuildsResponse> searchBuilds_Pre( - $grpc.ServiceCall call, $async.Future<$0.SearchBuildsRequest> request) async { - return searchBuilds(call, await request); - } - - $async.Future<$1.Build> updateBuild_Pre($grpc.ServiceCall call, $async.Future<$0.UpdateBuildRequest> request) async { - return updateBuild(call, await request); - } - - $async.Future<$1.Build> scheduleBuild_Pre( - $grpc.ServiceCall call, $async.Future<$0.ScheduleBuildRequest> request) async { - return scheduleBuild(call, await request); - } - - $async.Future<$1.Build> cancelBuild_Pre($grpc.ServiceCall call, $async.Future<$0.CancelBuildRequest> request) async { - return cancelBuild(call, await request); - } - - $async.Future<$0.BatchResponse> batch_Pre($grpc.ServiceCall call, $async.Future<$0.BatchRequest> request) async { - return batch(call, await request); - } - - $async.Future<$1.Build> createBuild_Pre($grpc.ServiceCall call, $async.Future<$0.CreateBuildRequest> request) async { - return createBuild(call, await request); - } - - $async.Future<$1.Build> synthesizeBuild_Pre( - $grpc.ServiceCall call, $async.Future<$0.SynthesizeBuildRequest> request) async { - return synthesizeBuild(call, await request); - } - - $async.Future<$1.Build> getBuildStatus_Pre( - $grpc.ServiceCall call, $async.Future<$0.GetBuildStatusRequest> request) async { - return getBuildStatus(call, await request); - } - - $async.Future<$0.StartBuildResponse> startBuild_Pre( - $grpc.ServiceCall call, $async.Future<$0.StartBuildRequest> request) async { - return startBuild(call, await request); - } - - $async.Future<$0.StartBuildTaskResponse> startBuildTask_Pre( - $grpc.ServiceCall call, $async.Future<$0.StartBuildTaskRequest> request) async { - return startBuildTask(call, await request); - } - - $async.Future<$1.Build> getBuild($grpc.ServiceCall call, $0.GetBuildRequest request); - $async.Future<$0.SearchBuildsResponse> searchBuilds($grpc.ServiceCall call, $0.SearchBuildsRequest request); - $async.Future<$1.Build> updateBuild($grpc.ServiceCall call, $0.UpdateBuildRequest request); - $async.Future<$1.Build> scheduleBuild($grpc.ServiceCall call, $0.ScheduleBuildRequest request); - $async.Future<$1.Build> cancelBuild($grpc.ServiceCall call, $0.CancelBuildRequest request); - $async.Future<$0.BatchResponse> batch($grpc.ServiceCall call, $0.BatchRequest request); - $async.Future<$1.Build> createBuild($grpc.ServiceCall call, $0.CreateBuildRequest request); - $async.Future<$1.Build> synthesizeBuild($grpc.ServiceCall call, $0.SynthesizeBuildRequest request); - $async.Future<$1.Build> getBuildStatus($grpc.ServiceCall call, $0.GetBuildStatusRequest request); - $async.Future<$0.StartBuildResponse> startBuild($grpc.ServiceCall call, $0.StartBuildRequest request); - $async.Future<$0.StartBuildTaskResponse> startBuildTask($grpc.ServiceCall call, $0.StartBuildTaskRequest request); -} diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builds_service.pbjson.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builds_service.pbjson.dart deleted file mode 100644 index 69b46e19b..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/builds_service.pbjson.dart +++ /dev/null @@ -1,572 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/builds_service.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:convert' as $convert; -import 'dart:core' as $core; -import 'dart:typed_data' as $typed_data; - -@$core.Deprecated('Use getBuildRequestDescriptor instead') -const GetBuildRequest$json = { - '1': 'GetBuildRequest', - '2': [ - {'1': 'id', '3': 1, '4': 1, '5': 3, '10': 'id'}, - {'1': 'builder', '3': 2, '4': 1, '5': 11, '6': '.buildbucket.v2.BuilderID', '10': 'builder'}, - {'1': 'build_number', '3': 3, '4': 1, '5': 5, '10': 'buildNumber'}, - { - '1': 'fields', - '3': 100, - '4': 1, - '5': 11, - '6': '.google.protobuf.FieldMask', - '8': {'3': true}, - '10': 'fields', - }, - {'1': 'mask', '3': 101, '4': 1, '5': 11, '6': '.buildbucket.v2.BuildMask', '10': 'mask'}, - ], -}; - -/// Descriptor for `GetBuildRequest`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List getBuildRequestDescriptor = - $convert.base64Decode('Cg9HZXRCdWlsZFJlcXVlc3QSDgoCaWQYASABKANSAmlkEjMKB2J1aWxkZXIYAiABKAsyGS5idW' - 'lsZGJ1Y2tldC52Mi5CdWlsZGVySURSB2J1aWxkZXISIQoMYnVpbGRfbnVtYmVyGAMgASgFUgti' - 'dWlsZE51bWJlchI2CgZmaWVsZHMYZCABKAsyGi5nb29nbGUucHJvdG9idWYuRmllbGRNYXNrQg' - 'IYAVIGZmllbGRzEi0KBG1hc2sYZSABKAsyGS5idWlsZGJ1Y2tldC52Mi5CdWlsZE1hc2tSBG1h' - 'c2s='); - -@$core.Deprecated('Use searchBuildsRequestDescriptor instead') -const SearchBuildsRequest$json = { - '1': 'SearchBuildsRequest', - '2': [ - {'1': 'predicate', '3': 1, '4': 1, '5': 11, '6': '.buildbucket.v2.BuildPredicate', '10': 'predicate'}, - { - '1': 'fields', - '3': 100, - '4': 1, - '5': 11, - '6': '.google.protobuf.FieldMask', - '8': {'3': true}, - '10': 'fields', - }, - {'1': 'mask', '3': 103, '4': 1, '5': 11, '6': '.buildbucket.v2.BuildMask', '10': 'mask'}, - {'1': 'page_size', '3': 101, '4': 1, '5': 5, '10': 'pageSize'}, - {'1': 'page_token', '3': 102, '4': 1, '5': 9, '10': 'pageToken'}, - ], -}; - -/// Descriptor for `SearchBuildsRequest`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List searchBuildsRequestDescriptor = - $convert.base64Decode('ChNTZWFyY2hCdWlsZHNSZXF1ZXN0EjwKCXByZWRpY2F0ZRgBIAEoCzIeLmJ1aWxkYnVja2V0Ln' - 'YyLkJ1aWxkUHJlZGljYXRlUglwcmVkaWNhdGUSNgoGZmllbGRzGGQgASgLMhouZ29vZ2xlLnBy' - 'b3RvYnVmLkZpZWxkTWFza0ICGAFSBmZpZWxkcxItCgRtYXNrGGcgASgLMhkuYnVpbGRidWNrZX' - 'QudjIuQnVpbGRNYXNrUgRtYXNrEhsKCXBhZ2Vfc2l6ZRhlIAEoBVIIcGFnZVNpemUSHQoKcGFn' - 'ZV90b2tlbhhmIAEoCVIJcGFnZVRva2Vu'); - -@$core.Deprecated('Use searchBuildsResponseDescriptor instead') -const SearchBuildsResponse$json = { - '1': 'SearchBuildsResponse', - '2': [ - {'1': 'builds', '3': 1, '4': 3, '5': 11, '6': '.buildbucket.v2.Build', '10': 'builds'}, - {'1': 'next_page_token', '3': 100, '4': 1, '5': 9, '10': 'nextPageToken'}, - ], -}; - -/// Descriptor for `SearchBuildsResponse`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List searchBuildsResponseDescriptor = - $convert.base64Decode('ChRTZWFyY2hCdWlsZHNSZXNwb25zZRItCgZidWlsZHMYASADKAsyFS5idWlsZGJ1Y2tldC52Mi' - '5CdWlsZFIGYnVpbGRzEiYKD25leHRfcGFnZV90b2tlbhhkIAEoCVINbmV4dFBhZ2VUb2tlbg=='); - -@$core.Deprecated('Use batchRequestDescriptor instead') -const BatchRequest$json = { - '1': 'BatchRequest', - '2': [ - {'1': 'requests', '3': 1, '4': 3, '5': 11, '6': '.buildbucket.v2.BatchRequest.Request', '10': 'requests'}, - ], - '3': [BatchRequest_Request$json], -}; - -@$core.Deprecated('Use batchRequestDescriptor instead') -const BatchRequest_Request$json = { - '1': 'Request', - '2': [ - {'1': 'get_build', '3': 1, '4': 1, '5': 11, '6': '.buildbucket.v2.GetBuildRequest', '9': 0, '10': 'getBuild'}, - { - '1': 'search_builds', - '3': 2, - '4': 1, - '5': 11, - '6': '.buildbucket.v2.SearchBuildsRequest', - '9': 0, - '10': 'searchBuilds' - }, - { - '1': 'schedule_build', - '3': 3, - '4': 1, - '5': 11, - '6': '.buildbucket.v2.ScheduleBuildRequest', - '9': 0, - '10': 'scheduleBuild' - }, - { - '1': 'cancel_build', - '3': 4, - '4': 1, - '5': 11, - '6': '.buildbucket.v2.CancelBuildRequest', - '9': 0, - '10': 'cancelBuild' - }, - { - '1': 'get_build_status', - '3': 5, - '4': 1, - '5': 11, - '6': '.buildbucket.v2.GetBuildStatusRequest', - '9': 0, - '10': 'getBuildStatus' - }, - ], - '8': [ - {'1': 'request'}, - ], -}; - -/// Descriptor for `BatchRequest`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List batchRequestDescriptor = - $convert.base64Decode('CgxCYXRjaFJlcXVlc3QSQAoIcmVxdWVzdHMYASADKAsyJC5idWlsZGJ1Y2tldC52Mi5CYXRjaF' - 'JlcXVlc3QuUmVxdWVzdFIIcmVxdWVzdHMaiwMKB1JlcXVlc3QSPgoJZ2V0X2J1aWxkGAEgASgL' - 'Mh8uYnVpbGRidWNrZXQudjIuR2V0QnVpbGRSZXF1ZXN0SABSCGdldEJ1aWxkEkoKDXNlYXJjaF' - '9idWlsZHMYAiABKAsyIy5idWlsZGJ1Y2tldC52Mi5TZWFyY2hCdWlsZHNSZXF1ZXN0SABSDHNl' - 'YXJjaEJ1aWxkcxJNCg5zY2hlZHVsZV9idWlsZBgDIAEoCzIkLmJ1aWxkYnVja2V0LnYyLlNjaG' - 'VkdWxlQnVpbGRSZXF1ZXN0SABSDXNjaGVkdWxlQnVpbGQSRwoMY2FuY2VsX2J1aWxkGAQgASgL' - 'MiIuYnVpbGRidWNrZXQudjIuQ2FuY2VsQnVpbGRSZXF1ZXN0SABSC2NhbmNlbEJ1aWxkElEKEG' - 'dldF9idWlsZF9zdGF0dXMYBSABKAsyJS5idWlsZGJ1Y2tldC52Mi5HZXRCdWlsZFN0YXR1c1Jl' - 'cXVlc3RIAFIOZ2V0QnVpbGRTdGF0dXNCCQoHcmVxdWVzdA=='); - -@$core.Deprecated('Use batchResponseDescriptor instead') -const BatchResponse$json = { - '1': 'BatchResponse', - '2': [ - {'1': 'responses', '3': 1, '4': 3, '5': 11, '6': '.buildbucket.v2.BatchResponse.Response', '10': 'responses'}, - ], - '3': [BatchResponse_Response$json], -}; - -@$core.Deprecated('Use batchResponseDescriptor instead') -const BatchResponse_Response$json = { - '1': 'Response', - '2': [ - {'1': 'get_build', '3': 1, '4': 1, '5': 11, '6': '.buildbucket.v2.Build', '9': 0, '10': 'getBuild'}, - { - '1': 'search_builds', - '3': 2, - '4': 1, - '5': 11, - '6': '.buildbucket.v2.SearchBuildsResponse', - '9': 0, - '10': 'searchBuilds' - }, - {'1': 'schedule_build', '3': 3, '4': 1, '5': 11, '6': '.buildbucket.v2.Build', '9': 0, '10': 'scheduleBuild'}, - {'1': 'cancel_build', '3': 4, '4': 1, '5': 11, '6': '.buildbucket.v2.Build', '9': 0, '10': 'cancelBuild'}, - {'1': 'get_build_status', '3': 5, '4': 1, '5': 11, '6': '.buildbucket.v2.Build', '9': 0, '10': 'getBuildStatus'}, - {'1': 'error', '3': 100, '4': 1, '5': 11, '6': '.google.rpc.Status', '9': 0, '10': 'error'}, - ], - '8': [ - {'1': 'response'}, - ], -}; - -/// Descriptor for `BatchResponse`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List batchResponseDescriptor = - $convert.base64Decode('Cg1CYXRjaFJlc3BvbnNlEkQKCXJlc3BvbnNlcxgBIAMoCzImLmJ1aWxkYnVja2V0LnYyLkJhdG' - 'NoUmVzcG9uc2UuUmVzcG9uc2VSCXJlc3BvbnNlcxqEAwoIUmVzcG9uc2USNAoJZ2V0X2J1aWxk' - 'GAEgASgLMhUuYnVpbGRidWNrZXQudjIuQnVpbGRIAFIIZ2V0QnVpbGQSSwoNc2VhcmNoX2J1aW' - 'xkcxgCIAEoCzIkLmJ1aWxkYnVja2V0LnYyLlNlYXJjaEJ1aWxkc1Jlc3BvbnNlSABSDHNlYXJj' - 'aEJ1aWxkcxI+Cg5zY2hlZHVsZV9idWlsZBgDIAEoCzIVLmJ1aWxkYnVja2V0LnYyLkJ1aWxkSA' - 'BSDXNjaGVkdWxlQnVpbGQSOgoMY2FuY2VsX2J1aWxkGAQgASgLMhUuYnVpbGRidWNrZXQudjIu' - 'QnVpbGRIAFILY2FuY2VsQnVpbGQSQQoQZ2V0X2J1aWxkX3N0YXR1cxgFIAEoCzIVLmJ1aWxkYn' - 'Vja2V0LnYyLkJ1aWxkSABSDmdldEJ1aWxkU3RhdHVzEioKBWVycm9yGGQgASgLMhIuZ29vZ2xl' - 'LnJwYy5TdGF0dXNIAFIFZXJyb3JCCgoIcmVzcG9uc2U='); - -@$core.Deprecated('Use updateBuildRequestDescriptor instead') -const UpdateBuildRequest$json = { - '1': 'UpdateBuildRequest', - '2': [ - {'1': 'build', '3': 1, '4': 1, '5': 11, '6': '.buildbucket.v2.Build', '10': 'build'}, - {'1': 'update_mask', '3': 2, '4': 1, '5': 11, '6': '.google.protobuf.FieldMask', '10': 'updateMask'}, - { - '1': 'fields', - '3': 100, - '4': 1, - '5': 11, - '6': '.google.protobuf.FieldMask', - '8': {'3': true}, - '10': 'fields', - }, - {'1': 'mask', '3': 101, '4': 1, '5': 11, '6': '.buildbucket.v2.BuildMask', '10': 'mask'}, - ], -}; - -/// Descriptor for `UpdateBuildRequest`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List updateBuildRequestDescriptor = - $convert.base64Decode('ChJVcGRhdGVCdWlsZFJlcXVlc3QSKwoFYnVpbGQYASABKAsyFS5idWlsZGJ1Y2tldC52Mi5CdW' - 'lsZFIFYnVpbGQSOwoLdXBkYXRlX21hc2sYAiABKAsyGi5nb29nbGUucHJvdG9idWYuRmllbGRN' - 'YXNrUgp1cGRhdGVNYXNrEjYKBmZpZWxkcxhkIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5GaWVsZE' - '1hc2tCAhgBUgZmaWVsZHMSLQoEbWFzaxhlIAEoCzIZLmJ1aWxkYnVja2V0LnYyLkJ1aWxkTWFz' - 'a1IEbWFzaw=='); - -@$core.Deprecated('Use scheduleBuildRequestDescriptor instead') -const ScheduleBuildRequest$json = { - '1': 'ScheduleBuildRequest', - '2': [ - {'1': 'request_id', '3': 1, '4': 1, '5': 9, '10': 'requestId'}, - {'1': 'template_build_id', '3': 2, '4': 1, '5': 3, '10': 'templateBuildId'}, - {'1': 'builder', '3': 3, '4': 1, '5': 11, '6': '.buildbucket.v2.BuilderID', '10': 'builder'}, - {'1': 'canary', '3': 4, '4': 1, '5': 14, '6': '.buildbucket.v2.Trinary', '10': 'canary'}, - {'1': 'experimental', '3': 5, '4': 1, '5': 14, '6': '.buildbucket.v2.Trinary', '10': 'experimental'}, - { - '1': 'experiments', - '3': 16, - '4': 3, - '5': 11, - '6': '.buildbucket.v2.ScheduleBuildRequest.ExperimentsEntry', - '10': 'experiments' - }, - {'1': 'properties', '3': 6, '4': 1, '5': 11, '6': '.google.protobuf.Struct', '10': 'properties'}, - {'1': 'gitiles_commit', '3': 7, '4': 1, '5': 11, '6': '.buildbucket.v2.GitilesCommit', '10': 'gitilesCommit'}, - {'1': 'gerrit_changes', '3': 8, '4': 3, '5': 11, '6': '.buildbucket.v2.GerritChange', '10': 'gerritChanges'}, - {'1': 'tags', '3': 9, '4': 3, '5': 11, '6': '.buildbucket.v2.StringPair', '10': 'tags'}, - {'1': 'dimensions', '3': 10, '4': 3, '5': 11, '6': '.buildbucket.v2.RequestedDimension', '10': 'dimensions'}, - {'1': 'priority', '3': 11, '4': 1, '5': 5, '10': 'priority'}, - {'1': 'notify', '3': 12, '4': 1, '5': 11, '6': '.buildbucket.v2.NotificationConfig', '10': 'notify'}, - { - '1': 'fields', - '3': 100, - '4': 1, - '5': 11, - '6': '.google.protobuf.FieldMask', - '8': {'3': true}, - '10': 'fields', - }, - {'1': 'mask', '3': 101, '4': 1, '5': 11, '6': '.buildbucket.v2.BuildMask', '10': 'mask'}, - {'1': 'critical', '3': 13, '4': 1, '5': 14, '6': '.buildbucket.v2.Trinary', '10': 'critical'}, - {'1': 'exe', '3': 14, '4': 1, '5': 11, '6': '.buildbucket.v2.Executable', '10': 'exe'}, - {'1': 'swarming', '3': 15, '4': 1, '5': 11, '6': '.buildbucket.v2.ScheduleBuildRequest.Swarming', '10': 'swarming'}, - {'1': 'scheduling_timeout', '3': 17, '4': 1, '5': 11, '6': '.google.protobuf.Duration', '10': 'schedulingTimeout'}, - {'1': 'execution_timeout', '3': 18, '4': 1, '5': 11, '6': '.google.protobuf.Duration', '10': 'executionTimeout'}, - {'1': 'grace_period', '3': 19, '4': 1, '5': 11, '6': '.google.protobuf.Duration', '10': 'gracePeriod'}, - {'1': 'dry_run', '3': 20, '4': 1, '5': 8, '10': 'dryRun'}, - {'1': 'can_outlive_parent', '3': 21, '4': 1, '5': 14, '6': '.buildbucket.v2.Trinary', '10': 'canOutliveParent'}, - {'1': 'retriable', '3': 22, '4': 1, '5': 14, '6': '.buildbucket.v2.Trinary', '10': 'retriable'}, - { - '1': 'shadow_input', - '3': 23, - '4': 1, - '5': 11, - '6': '.buildbucket.v2.ScheduleBuildRequest.ShadowInput', - '10': 'shadowInput' - }, - ], - '3': [ - ScheduleBuildRequest_ExperimentsEntry$json, - ScheduleBuildRequest_Swarming$json, - ScheduleBuildRequest_ShadowInput$json - ], -}; - -@$core.Deprecated('Use scheduleBuildRequestDescriptor instead') -const ScheduleBuildRequest_ExperimentsEntry$json = { - '1': 'ExperimentsEntry', - '2': [ - {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, - {'1': 'value', '3': 2, '4': 1, '5': 8, '10': 'value'}, - ], - '7': {'7': true}, -}; - -@$core.Deprecated('Use scheduleBuildRequestDescriptor instead') -const ScheduleBuildRequest_Swarming$json = { - '1': 'Swarming', - '2': [ - {'1': 'parent_run_id', '3': 1, '4': 1, '5': 9, '10': 'parentRunId'}, - ], -}; - -@$core.Deprecated('Use scheduleBuildRequestDescriptor instead') -const ScheduleBuildRequest_ShadowInput$json = { - '1': 'ShadowInput', -}; - -/// Descriptor for `ScheduleBuildRequest`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List scheduleBuildRequestDescriptor = - $convert.base64Decode('ChRTY2hlZHVsZUJ1aWxkUmVxdWVzdBIdCgpyZXF1ZXN0X2lkGAEgASgJUglyZXF1ZXN0SWQSKg' - 'oRdGVtcGxhdGVfYnVpbGRfaWQYAiABKANSD3RlbXBsYXRlQnVpbGRJZBIzCgdidWlsZGVyGAMg' - 'ASgLMhkuYnVpbGRidWNrZXQudjIuQnVpbGRlcklEUgdidWlsZGVyEi8KBmNhbmFyeRgEIAEoDj' - 'IXLmJ1aWxkYnVja2V0LnYyLlRyaW5hcnlSBmNhbmFyeRI7CgxleHBlcmltZW50YWwYBSABKA4y' - 'Fy5idWlsZGJ1Y2tldC52Mi5UcmluYXJ5UgxleHBlcmltZW50YWwSVwoLZXhwZXJpbWVudHMYEC' - 'ADKAsyNS5idWlsZGJ1Y2tldC52Mi5TY2hlZHVsZUJ1aWxkUmVxdWVzdC5FeHBlcmltZW50c0Vu' - 'dHJ5UgtleHBlcmltZW50cxI3Cgpwcm9wZXJ0aWVzGAYgASgLMhcuZ29vZ2xlLnByb3RvYnVmLl' - 'N0cnVjdFIKcHJvcGVydGllcxJECg5naXRpbGVzX2NvbW1pdBgHIAEoCzIdLmJ1aWxkYnVja2V0' - 'LnYyLkdpdGlsZXNDb21taXRSDWdpdGlsZXNDb21taXQSQwoOZ2Vycml0X2NoYW5nZXMYCCADKA' - 'syHC5idWlsZGJ1Y2tldC52Mi5HZXJyaXRDaGFuZ2VSDWdlcnJpdENoYW5nZXMSLgoEdGFncxgJ' - 'IAMoCzIaLmJ1aWxkYnVja2V0LnYyLlN0cmluZ1BhaXJSBHRhZ3MSQgoKZGltZW5zaW9ucxgKIA' - 'MoCzIiLmJ1aWxkYnVja2V0LnYyLlJlcXVlc3RlZERpbWVuc2lvblIKZGltZW5zaW9ucxIaCghw' - 'cmlvcml0eRgLIAEoBVIIcHJpb3JpdHkSOgoGbm90aWZ5GAwgASgLMiIuYnVpbGRidWNrZXQudj' - 'IuTm90aWZpY2F0aW9uQ29uZmlnUgZub3RpZnkSNgoGZmllbGRzGGQgASgLMhouZ29vZ2xlLnBy' - 'b3RvYnVmLkZpZWxkTWFza0ICGAFSBmZpZWxkcxItCgRtYXNrGGUgASgLMhkuYnVpbGRidWNrZX' - 'QudjIuQnVpbGRNYXNrUgRtYXNrEjMKCGNyaXRpY2FsGA0gASgOMhcuYnVpbGRidWNrZXQudjIu' - 'VHJpbmFyeVIIY3JpdGljYWwSLAoDZXhlGA4gASgLMhouYnVpbGRidWNrZXQudjIuRXhlY3V0YW' - 'JsZVIDZXhlEkkKCHN3YXJtaW5nGA8gASgLMi0uYnVpbGRidWNrZXQudjIuU2NoZWR1bGVCdWls' - 'ZFJlcXVlc3QuU3dhcm1pbmdSCHN3YXJtaW5nEkgKEnNjaGVkdWxpbmdfdGltZW91dBgRIAEoCz' - 'IZLmdvb2dsZS5wcm90b2J1Zi5EdXJhdGlvblIRc2NoZWR1bGluZ1RpbWVvdXQSRgoRZXhlY3V0' - 'aW9uX3RpbWVvdXQYEiABKAsyGS5nb29nbGUucHJvdG9idWYuRHVyYXRpb25SEGV4ZWN1dGlvbl' - 'RpbWVvdXQSPAoMZ3JhY2VfcGVyaW9kGBMgASgLMhkuZ29vZ2xlLnByb3RvYnVmLkR1cmF0aW9u' - 'UgtncmFjZVBlcmlvZBIXCgdkcnlfcnVuGBQgASgIUgZkcnlSdW4SRQoSY2FuX291dGxpdmVfcG' - 'FyZW50GBUgASgOMhcuYnVpbGRidWNrZXQudjIuVHJpbmFyeVIQY2FuT3V0bGl2ZVBhcmVudBI1' - 'CglyZXRyaWFibGUYFiABKA4yFy5idWlsZGJ1Y2tldC52Mi5UcmluYXJ5UglyZXRyaWFibGUSUw' - 'oMc2hhZG93X2lucHV0GBcgASgLMjAuYnVpbGRidWNrZXQudjIuU2NoZWR1bGVCdWlsZFJlcXVl' - 'c3QuU2hhZG93SW5wdXRSC3NoYWRvd0lucHV0Gj4KEEV4cGVyaW1lbnRzRW50cnkSEAoDa2V5GA' - 'EgASgJUgNrZXkSFAoFdmFsdWUYAiABKAhSBXZhbHVlOgI4ARouCghTd2FybWluZxIiCg1wYXJl' - 'bnRfcnVuX2lkGAEgASgJUgtwYXJlbnRSdW5JZBoNCgtTaGFkb3dJbnB1dA=='); - -@$core.Deprecated('Use cancelBuildRequestDescriptor instead') -const CancelBuildRequest$json = { - '1': 'CancelBuildRequest', - '2': [ - {'1': 'id', '3': 1, '4': 1, '5': 3, '10': 'id'}, - {'1': 'summary_markdown', '3': 2, '4': 1, '5': 9, '10': 'summaryMarkdown'}, - { - '1': 'fields', - '3': 100, - '4': 1, - '5': 11, - '6': '.google.protobuf.FieldMask', - '8': {'3': true}, - '10': 'fields', - }, - {'1': 'mask', '3': 101, '4': 1, '5': 11, '6': '.buildbucket.v2.BuildMask', '10': 'mask'}, - ], -}; - -/// Descriptor for `CancelBuildRequest`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List cancelBuildRequestDescriptor = - $convert.base64Decode('ChJDYW5jZWxCdWlsZFJlcXVlc3QSDgoCaWQYASABKANSAmlkEikKEHN1bW1hcnlfbWFya2Rvd2' - '4YAiABKAlSD3N1bW1hcnlNYXJrZG93bhI2CgZmaWVsZHMYZCABKAsyGi5nb29nbGUucHJvdG9i' - 'dWYuRmllbGRNYXNrQgIYAVIGZmllbGRzEi0KBG1hc2sYZSABKAsyGS5idWlsZGJ1Y2tldC52Mi' - '5CdWlsZE1hc2tSBG1hc2s='); - -@$core.Deprecated('Use createBuildRequestDescriptor instead') -const CreateBuildRequest$json = { - '1': 'CreateBuildRequest', - '2': [ - {'1': 'build', '3': 1, '4': 1, '5': 11, '6': '.buildbucket.v2.Build', '8': {}, '10': 'build'}, - {'1': 'request_id', '3': 2, '4': 1, '5': 9, '10': 'requestId'}, - {'1': 'mask', '3': 3, '4': 1, '5': 11, '6': '.buildbucket.v2.BuildMask', '10': 'mask'}, - ], -}; - -/// Descriptor for `CreateBuildRequest`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List createBuildRequestDescriptor = - $convert.base64Decode('ChJDcmVhdGVCdWlsZFJlcXVlc3QSMAoFYnVpbGQYASABKAsyFS5idWlsZGJ1Y2tldC52Mi5CdW' - 'lsZEID4EECUgVidWlsZBIdCgpyZXF1ZXN0X2lkGAIgASgJUglyZXF1ZXN0SWQSLQoEbWFzaxgD' - 'IAEoCzIZLmJ1aWxkYnVja2V0LnYyLkJ1aWxkTWFza1IEbWFzaw=='); - -@$core.Deprecated('Use synthesizeBuildRequestDescriptor instead') -const SynthesizeBuildRequest$json = { - '1': 'SynthesizeBuildRequest', - '2': [ - {'1': 'template_build_id', '3': 1, '4': 1, '5': 3, '10': 'templateBuildId'}, - {'1': 'builder', '3': 2, '4': 1, '5': 11, '6': '.buildbucket.v2.BuilderID', '10': 'builder'}, - { - '1': 'experiments', - '3': 3, - '4': 3, - '5': 11, - '6': '.buildbucket.v2.SynthesizeBuildRequest.ExperimentsEntry', - '10': 'experiments' - }, - ], - '3': [SynthesizeBuildRequest_ExperimentsEntry$json], -}; - -@$core.Deprecated('Use synthesizeBuildRequestDescriptor instead') -const SynthesizeBuildRequest_ExperimentsEntry$json = { - '1': 'ExperimentsEntry', - '2': [ - {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, - {'1': 'value', '3': 2, '4': 1, '5': 8, '10': 'value'}, - ], - '7': {'7': true}, -}; - -/// Descriptor for `SynthesizeBuildRequest`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List synthesizeBuildRequestDescriptor = - $convert.base64Decode('ChZTeW50aGVzaXplQnVpbGRSZXF1ZXN0EioKEXRlbXBsYXRlX2J1aWxkX2lkGAEgASgDUg90ZW' - '1wbGF0ZUJ1aWxkSWQSMwoHYnVpbGRlchgCIAEoCzIZLmJ1aWxkYnVja2V0LnYyLkJ1aWxkZXJJ' - 'RFIHYnVpbGRlchJZCgtleHBlcmltZW50cxgDIAMoCzI3LmJ1aWxkYnVja2V0LnYyLlN5bnRoZX' - 'NpemVCdWlsZFJlcXVlc3QuRXhwZXJpbWVudHNFbnRyeVILZXhwZXJpbWVudHMaPgoQRXhwZXJp' - 'bWVudHNFbnRyeRIQCgNrZXkYASABKAlSA2tleRIUCgV2YWx1ZRgCIAEoCFIFdmFsdWU6AjgB'); - -@$core.Deprecated('Use startBuildRequestDescriptor instead') -const StartBuildRequest$json = { - '1': 'StartBuildRequest', - '2': [ - {'1': 'request_id', '3': 1, '4': 1, '5': 9, '8': {}, '10': 'requestId'}, - {'1': 'build_id', '3': 2, '4': 1, '5': 3, '8': {}, '10': 'buildId'}, - {'1': 'task_id', '3': 3, '4': 1, '5': 9, '8': {}, '10': 'taskId'}, - ], -}; - -/// Descriptor for `StartBuildRequest`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List startBuildRequestDescriptor = - $convert.base64Decode('ChFTdGFydEJ1aWxkUmVxdWVzdBIiCgpyZXF1ZXN0X2lkGAEgASgJQgPgQQJSCXJlcXVlc3RJZB' - 'IeCghidWlsZF9pZBgCIAEoA0ID4EECUgdidWlsZElkEhwKB3Rhc2tfaWQYAyABKAlCA+BBAlIG' - 'dGFza0lk'); - -@$core.Deprecated('Use startBuildResponseDescriptor instead') -const StartBuildResponse$json = { - '1': 'StartBuildResponse', - '2': [ - {'1': 'build', '3': 1, '4': 1, '5': 11, '6': '.buildbucket.v2.Build', '10': 'build'}, - {'1': 'update_build_token', '3': 2, '4': 1, '5': 9, '10': 'updateBuildToken'}, - ], -}; - -/// Descriptor for `StartBuildResponse`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List startBuildResponseDescriptor = - $convert.base64Decode('ChJTdGFydEJ1aWxkUmVzcG9uc2USKwoFYnVpbGQYASABKAsyFS5idWlsZGJ1Y2tldC52Mi5CdW' - 'lsZFIFYnVpbGQSLAoSdXBkYXRlX2J1aWxkX3Rva2VuGAIgASgJUhB1cGRhdGVCdWlsZFRva2Vu'); - -@$core.Deprecated('Use getBuildStatusRequestDescriptor instead') -const GetBuildStatusRequest$json = { - '1': 'GetBuildStatusRequest', - '2': [ - {'1': 'id', '3': 1, '4': 1, '5': 3, '10': 'id'}, - {'1': 'builder', '3': 2, '4': 1, '5': 11, '6': '.buildbucket.v2.BuilderID', '10': 'builder'}, - {'1': 'build_number', '3': 3, '4': 1, '5': 5, '10': 'buildNumber'}, - ], -}; - -/// Descriptor for `GetBuildStatusRequest`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List getBuildStatusRequestDescriptor = - $convert.base64Decode('ChVHZXRCdWlsZFN0YXR1c1JlcXVlc3QSDgoCaWQYASABKANSAmlkEjMKB2J1aWxkZXIYAiABKA' - 'syGS5idWlsZGJ1Y2tldC52Mi5CdWlsZGVySURSB2J1aWxkZXISIQoMYnVpbGRfbnVtYmVyGAMg' - 'ASgFUgtidWlsZE51bWJlcg=='); - -@$core.Deprecated('Use buildMaskDescriptor instead') -const BuildMask$json = { - '1': 'BuildMask', - '2': [ - {'1': 'fields', '3': 1, '4': 1, '5': 11, '6': '.google.protobuf.FieldMask', '10': 'fields'}, - {'1': 'input_properties', '3': 2, '4': 3, '5': 11, '6': '.structmask.StructMask', '10': 'inputProperties'}, - {'1': 'output_properties', '3': 3, '4': 3, '5': 11, '6': '.structmask.StructMask', '10': 'outputProperties'}, - {'1': 'requested_properties', '3': 4, '4': 3, '5': 11, '6': '.structmask.StructMask', '10': 'requestedProperties'}, - {'1': 'all_fields', '3': 5, '4': 1, '5': 8, '10': 'allFields'}, - {'1': 'step_status', '3': 6, '4': 3, '5': 14, '6': '.buildbucket.v2.Status', '10': 'stepStatus'}, - ], -}; - -/// Descriptor for `BuildMask`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List buildMaskDescriptor = - $convert.base64Decode('CglCdWlsZE1hc2sSMgoGZmllbGRzGAEgASgLMhouZ29vZ2xlLnByb3RvYnVmLkZpZWxkTWFza1' - 'IGZmllbGRzEkEKEGlucHV0X3Byb3BlcnRpZXMYAiADKAsyFi5zdHJ1Y3RtYXNrLlN0cnVjdE1h' - 'c2tSD2lucHV0UHJvcGVydGllcxJDChFvdXRwdXRfcHJvcGVydGllcxgDIAMoCzIWLnN0cnVjdG' - '1hc2suU3RydWN0TWFza1IQb3V0cHV0UHJvcGVydGllcxJJChRyZXF1ZXN0ZWRfcHJvcGVydGll' - 'cxgEIAMoCzIWLnN0cnVjdG1hc2suU3RydWN0TWFza1ITcmVxdWVzdGVkUHJvcGVydGllcxIdCg' - 'phbGxfZmllbGRzGAUgASgIUglhbGxGaWVsZHMSNwoLc3RlcF9zdGF0dXMYBiADKA4yFi5idWls' - 'ZGJ1Y2tldC52Mi5TdGF0dXNSCnN0ZXBTdGF0dXM='); - -@$core.Deprecated('Use buildPredicateDescriptor instead') -const BuildPredicate$json = { - '1': 'BuildPredicate', - '2': [ - {'1': 'builder', '3': 1, '4': 1, '5': 11, '6': '.buildbucket.v2.BuilderID', '10': 'builder'}, - {'1': 'status', '3': 2, '4': 1, '5': 14, '6': '.buildbucket.v2.Status', '10': 'status'}, - {'1': 'gerrit_changes', '3': 3, '4': 3, '5': 11, '6': '.buildbucket.v2.GerritChange', '10': 'gerritChanges'}, - { - '1': 'output_gitiles_commit', - '3': 4, - '4': 1, - '5': 11, - '6': '.buildbucket.v2.GitilesCommit', - '10': 'outputGitilesCommit' - }, - {'1': 'created_by', '3': 5, '4': 1, '5': 9, '10': 'createdBy'}, - {'1': 'tags', '3': 6, '4': 3, '5': 11, '6': '.buildbucket.v2.StringPair', '10': 'tags'}, - {'1': 'create_time', '3': 7, '4': 1, '5': 11, '6': '.buildbucket.v2.TimeRange', '10': 'createTime'}, - {'1': 'include_experimental', '3': 8, '4': 1, '5': 8, '10': 'includeExperimental'}, - {'1': 'build', '3': 9, '4': 1, '5': 11, '6': '.buildbucket.v2.BuildRange', '10': 'build'}, - {'1': 'canary', '3': 10, '4': 1, '5': 14, '6': '.buildbucket.v2.Trinary', '10': 'canary'}, - {'1': 'experiments', '3': 11, '4': 3, '5': 9, '10': 'experiments'}, - {'1': 'descendant_of', '3': 12, '4': 1, '5': 3, '10': 'descendantOf'}, - {'1': 'child_of', '3': 13, '4': 1, '5': 3, '10': 'childOf'}, - ], -}; - -/// Descriptor for `BuildPredicate`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List buildPredicateDescriptor = - $convert.base64Decode('Cg5CdWlsZFByZWRpY2F0ZRIzCgdidWlsZGVyGAEgASgLMhkuYnVpbGRidWNrZXQudjIuQnVpbG' - 'RlcklEUgdidWlsZGVyEi4KBnN0YXR1cxgCIAEoDjIWLmJ1aWxkYnVja2V0LnYyLlN0YXR1c1IG' - 'c3RhdHVzEkMKDmdlcnJpdF9jaGFuZ2VzGAMgAygLMhwuYnVpbGRidWNrZXQudjIuR2Vycml0Q2' - 'hhbmdlUg1nZXJyaXRDaGFuZ2VzElEKFW91dHB1dF9naXRpbGVzX2NvbW1pdBgEIAEoCzIdLmJ1' - 'aWxkYnVja2V0LnYyLkdpdGlsZXNDb21taXRSE291dHB1dEdpdGlsZXNDb21taXQSHQoKY3JlYX' - 'RlZF9ieRgFIAEoCVIJY3JlYXRlZEJ5Ei4KBHRhZ3MYBiADKAsyGi5idWlsZGJ1Y2tldC52Mi5T' - 'dHJpbmdQYWlyUgR0YWdzEjoKC2NyZWF0ZV90aW1lGAcgASgLMhkuYnVpbGRidWNrZXQudjIuVG' - 'ltZVJhbmdlUgpjcmVhdGVUaW1lEjEKFGluY2x1ZGVfZXhwZXJpbWVudGFsGAggASgIUhNpbmNs' - 'dWRlRXhwZXJpbWVudGFsEjAKBWJ1aWxkGAkgASgLMhouYnVpbGRidWNrZXQudjIuQnVpbGRSYW' - '5nZVIFYnVpbGQSLwoGY2FuYXJ5GAogASgOMhcuYnVpbGRidWNrZXQudjIuVHJpbmFyeVIGY2Fu' - 'YXJ5EiAKC2V4cGVyaW1lbnRzGAsgAygJUgtleHBlcmltZW50cxIjCg1kZXNjZW5kYW50X29mGA' - 'wgASgDUgxkZXNjZW5kYW50T2YSGQoIY2hpbGRfb2YYDSABKANSB2NoaWxkT2Y='); - -@$core.Deprecated('Use buildRangeDescriptor instead') -const BuildRange$json = { - '1': 'BuildRange', - '2': [ - {'1': 'start_build_id', '3': 1, '4': 1, '5': 3, '10': 'startBuildId'}, - {'1': 'end_build_id', '3': 2, '4': 1, '5': 3, '10': 'endBuildId'}, - ], -}; - -/// Descriptor for `BuildRange`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List buildRangeDescriptor = - $convert.base64Decode('CgpCdWlsZFJhbmdlEiQKDnN0YXJ0X2J1aWxkX2lkGAEgASgDUgxzdGFydEJ1aWxkSWQSIAoMZW' - '5kX2J1aWxkX2lkGAIgASgDUgplbmRCdWlsZElk'); - -@$core.Deprecated('Use startBuildTaskRequestDescriptor instead') -const StartBuildTaskRequest$json = { - '1': 'StartBuildTaskRequest', - '2': [ - {'1': 'request_id', '3': 1, '4': 1, '5': 9, '8': {}, '10': 'requestId'}, - {'1': 'build_id', '3': 2, '4': 1, '5': 3, '8': {}, '10': 'buildId'}, - {'1': 'task', '3': 3, '4': 1, '5': 11, '6': '.buildbucket.v2.Task', '8': {}, '10': 'task'}, - ], -}; - -/// Descriptor for `StartBuildTaskRequest`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List startBuildTaskRequestDescriptor = - $convert.base64Decode('ChVTdGFydEJ1aWxkVGFza1JlcXVlc3QSIgoKcmVxdWVzdF9pZBgBIAEoCUID4EECUglyZXF1ZX' - 'N0SWQSHgoIYnVpbGRfaWQYAiABKANCA+BBAlIHYnVpbGRJZBItCgR0YXNrGAMgASgLMhQuYnVp' - 'bGRidWNrZXQudjIuVGFza0ID4EECUgR0YXNr'); - -@$core.Deprecated('Use startBuildTaskResponseDescriptor instead') -const StartBuildTaskResponse$json = { - '1': 'StartBuildTaskResponse', - '2': [ - {'1': 'secrets', '3': 1, '4': 1, '5': 11, '6': '.buildbucket.v2.BuildSecrets', '10': 'secrets'}, - {'1': 'pubsub_topic', '3': 2, '4': 1, '5': 9, '10': 'pubsubTopic'}, - ], -}; - -/// Descriptor for `StartBuildTaskResponse`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List startBuildTaskResponseDescriptor = - $convert.base64Decode('ChZTdGFydEJ1aWxkVGFza1Jlc3BvbnNlEjYKB3NlY3JldHMYASABKAsyHC5idWlsZGJ1Y2tldC' - '52Mi5CdWlsZFNlY3JldHNSB3NlY3JldHMSIQoMcHVic3ViX3RvcGljGAIgASgJUgtwdWJzdWJU' - 'b3BpYw=='); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/common.pb.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/common.pb.dart deleted file mode 100644 index c56d81374..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/common.pb.dart +++ /dev/null @@ -1,890 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/common.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:core' as $core; - -import 'package:fixnum/fixnum.dart' as $fixnum; -import 'package:protobuf/protobuf.dart' as $pb; - -import '../../../../google/protobuf/duration.pb.dart' as $1; -import '../../../../google/protobuf/timestamp.pb.dart' as $0; - -export 'common.pbenum.dart'; - -class Executable extends $pb.GeneratedMessage { - factory Executable() => create(); - Executable._() : super(); - factory Executable.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory Executable.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Executable', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'cipdPackage') - ..aOS(2, _omitFieldNames ? '' : 'cipdVersion') - ..pPS(3, _omitFieldNames ? '' : 'cmd') - ..pPS(4, _omitFieldNames ? '' : 'wrapper') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - Executable clone() => Executable()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - Executable copyWith(void Function(Executable) updates) => - super.copyWith((message) => updates(message as Executable)) as Executable; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static Executable create() => Executable._(); - Executable createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Executable getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Executable? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get cipdPackage => $_getSZ(0); - @$pb.TagNumber(1) - set cipdPackage($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasCipdPackage() => $_has(0); - @$pb.TagNumber(1) - void clearCipdPackage() => clearField(1); - - @$pb.TagNumber(2) - $core.String get cipdVersion => $_getSZ(1); - @$pb.TagNumber(2) - set cipdVersion($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasCipdVersion() => $_has(1); - @$pb.TagNumber(2) - void clearCipdVersion() => clearField(2); - - @$pb.TagNumber(3) - $core.List<$core.String> get cmd => $_getList(2); - - @$pb.TagNumber(4) - $core.List<$core.String> get wrapper => $_getList(3); -} - -class StatusDetails_ResourceExhaustion extends $pb.GeneratedMessage { - factory StatusDetails_ResourceExhaustion() => create(); - StatusDetails_ResourceExhaustion._() : super(); - factory StatusDetails_ResourceExhaustion.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory StatusDetails_ResourceExhaustion.fromJson($core.String i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'StatusDetails.ResourceExhaustion', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - StatusDetails_ResourceExhaustion clone() => StatusDetails_ResourceExhaustion()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - StatusDetails_ResourceExhaustion copyWith(void Function(StatusDetails_ResourceExhaustion) updates) => - super.copyWith((message) => updates(message as StatusDetails_ResourceExhaustion)) - as StatusDetails_ResourceExhaustion; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static StatusDetails_ResourceExhaustion create() => StatusDetails_ResourceExhaustion._(); - StatusDetails_ResourceExhaustion createEmptyInstance() => create(); - static $pb.PbList createRepeated() => - $pb.PbList(); - @$core.pragma('dart2js:noInline') - static StatusDetails_ResourceExhaustion getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static StatusDetails_ResourceExhaustion? _defaultInstance; -} - -class StatusDetails_Timeout extends $pb.GeneratedMessage { - factory StatusDetails_Timeout() => create(); - StatusDetails_Timeout._() : super(); - factory StatusDetails_Timeout.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory StatusDetails_Timeout.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'StatusDetails.Timeout', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - StatusDetails_Timeout clone() => StatusDetails_Timeout()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - StatusDetails_Timeout copyWith(void Function(StatusDetails_Timeout) updates) => - super.copyWith((message) => updates(message as StatusDetails_Timeout)) as StatusDetails_Timeout; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static StatusDetails_Timeout create() => StatusDetails_Timeout._(); - StatusDetails_Timeout createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static StatusDetails_Timeout getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static StatusDetails_Timeout? _defaultInstance; -} - -class StatusDetails extends $pb.GeneratedMessage { - factory StatusDetails() => create(); - StatusDetails._() : super(); - factory StatusDetails.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory StatusDetails.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'StatusDetails', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOM(3, _omitFieldNames ? '' : 'resourceExhaustion', - subBuilder: StatusDetails_ResourceExhaustion.create) - ..aOM(4, _omitFieldNames ? '' : 'timeout', subBuilder: StatusDetails_Timeout.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - StatusDetails clone() => StatusDetails()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - StatusDetails copyWith(void Function(StatusDetails) updates) => - super.copyWith((message) => updates(message as StatusDetails)) as StatusDetails; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static StatusDetails create() => StatusDetails._(); - StatusDetails createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static StatusDetails getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static StatusDetails? _defaultInstance; - - @$pb.TagNumber(3) - StatusDetails_ResourceExhaustion get resourceExhaustion => $_getN(0); - @$pb.TagNumber(3) - set resourceExhaustion(StatusDetails_ResourceExhaustion v) { - setField(3, v); - } - - @$pb.TagNumber(3) - $core.bool hasResourceExhaustion() => $_has(0); - @$pb.TagNumber(3) - void clearResourceExhaustion() => clearField(3); - @$pb.TagNumber(3) - StatusDetails_ResourceExhaustion ensureResourceExhaustion() => $_ensure(0); - - @$pb.TagNumber(4) - StatusDetails_Timeout get timeout => $_getN(1); - @$pb.TagNumber(4) - set timeout(StatusDetails_Timeout v) { - setField(4, v); - } - - @$pb.TagNumber(4) - $core.bool hasTimeout() => $_has(1); - @$pb.TagNumber(4) - void clearTimeout() => clearField(4); - @$pb.TagNumber(4) - StatusDetails_Timeout ensureTimeout() => $_ensure(1); -} - -class Log extends $pb.GeneratedMessage { - factory Log() => create(); - Log._() : super(); - factory Log.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory Log.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Log', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'name') - ..aOS(2, _omitFieldNames ? '' : 'viewUrl') - ..aOS(3, _omitFieldNames ? '' : 'url') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - Log clone() => Log()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - Log copyWith(void Function(Log) updates) => super.copyWith((message) => updates(message as Log)) as Log; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static Log create() => Log._(); - Log createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Log getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Log? _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.String get viewUrl => $_getSZ(1); - @$pb.TagNumber(2) - set viewUrl($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasViewUrl() => $_has(1); - @$pb.TagNumber(2) - void clearViewUrl() => clearField(2); - - @$pb.TagNumber(3) - $core.String get url => $_getSZ(2); - @$pb.TagNumber(3) - set url($core.String v) { - $_setString(2, v); - } - - @$pb.TagNumber(3) - $core.bool hasUrl() => $_has(2); - @$pb.TagNumber(3) - void clearUrl() => clearField(3); -} - -class GerritChange extends $pb.GeneratedMessage { - factory GerritChange() => create(); - GerritChange._() : super(); - factory GerritChange.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory GerritChange.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'GerritChange', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'host') - ..aOS(2, _omitFieldNames ? '' : 'project') - ..aInt64(3, _omitFieldNames ? '' : 'change') - ..aInt64(4, _omitFieldNames ? '' : 'patchset') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - GerritChange clone() => GerritChange()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - GerritChange copyWith(void Function(GerritChange) updates) => - super.copyWith((message) => updates(message as GerritChange)) as GerritChange; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static GerritChange create() => GerritChange._(); - GerritChange createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static GerritChange getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static GerritChange? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get host => $_getSZ(0); - @$pb.TagNumber(1) - set host($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasHost() => $_has(0); - @$pb.TagNumber(1) - void clearHost() => clearField(1); - - @$pb.TagNumber(2) - $core.String get project => $_getSZ(1); - @$pb.TagNumber(2) - set project($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasProject() => $_has(1); - @$pb.TagNumber(2) - void clearProject() => clearField(2); - - @$pb.TagNumber(3) - $fixnum.Int64 get change => $_getI64(2); - @$pb.TagNumber(3) - set change($fixnum.Int64 v) { - $_setInt64(2, v); - } - - @$pb.TagNumber(3) - $core.bool hasChange() => $_has(2); - @$pb.TagNumber(3) - void clearChange() => clearField(3); - - @$pb.TagNumber(4) - $fixnum.Int64 get patchset => $_getI64(3); - @$pb.TagNumber(4) - set patchset($fixnum.Int64 v) { - $_setInt64(3, v); - } - - @$pb.TagNumber(4) - $core.bool hasPatchset() => $_has(3); - @$pb.TagNumber(4) - void clearPatchset() => clearField(4); -} - -class GitilesCommit extends $pb.GeneratedMessage { - factory GitilesCommit() => create(); - GitilesCommit._() : super(); - factory GitilesCommit.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory GitilesCommit.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'GitilesCommit', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'host') - ..aOS(2, _omitFieldNames ? '' : 'project') - ..aOS(3, _omitFieldNames ? '' : 'id') - ..aOS(4, _omitFieldNames ? '' : 'ref') - ..a<$core.int>(5, _omitFieldNames ? '' : 'position', $pb.PbFieldType.OU3) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - GitilesCommit clone() => GitilesCommit()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - GitilesCommit copyWith(void Function(GitilesCommit) updates) => - super.copyWith((message) => updates(message as GitilesCommit)) as GitilesCommit; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static GitilesCommit create() => GitilesCommit._(); - GitilesCommit createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static GitilesCommit getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static GitilesCommit? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get host => $_getSZ(0); - @$pb.TagNumber(1) - set host($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasHost() => $_has(0); - @$pb.TagNumber(1) - void clearHost() => clearField(1); - - @$pb.TagNumber(2) - $core.String get project => $_getSZ(1); - @$pb.TagNumber(2) - set project($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasProject() => $_has(1); - @$pb.TagNumber(2) - void clearProject() => clearField(2); - - @$pb.TagNumber(3) - $core.String get id => $_getSZ(2); - @$pb.TagNumber(3) - set id($core.String v) { - $_setString(2, v); - } - - @$pb.TagNumber(3) - $core.bool hasId() => $_has(2); - @$pb.TagNumber(3) - void clearId() => clearField(3); - - @$pb.TagNumber(4) - $core.String get ref => $_getSZ(3); - @$pb.TagNumber(4) - set ref($core.String v) { - $_setString(3, v); - } - - @$pb.TagNumber(4) - $core.bool hasRef() => $_has(3); - @$pb.TagNumber(4) - void clearRef() => clearField(4); - - @$pb.TagNumber(5) - $core.int get position => $_getIZ(4); - @$pb.TagNumber(5) - set position($core.int v) { - $_setUnsignedInt32(4, v); - } - - @$pb.TagNumber(5) - $core.bool hasPosition() => $_has(4); - @$pb.TagNumber(5) - void clearPosition() => clearField(5); -} - -class StringPair extends $pb.GeneratedMessage { - factory StringPair() => create(); - StringPair._() : super(); - factory StringPair.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory StringPair.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'StringPair', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'key') - ..aOS(2, _omitFieldNames ? '' : 'value') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - StringPair clone() => StringPair()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - StringPair copyWith(void Function(StringPair) updates) => - super.copyWith((message) => updates(message as StringPair)) as StringPair; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static StringPair create() => StringPair._(); - StringPair createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static StringPair getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static StringPair? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get key => $_getSZ(0); - @$pb.TagNumber(1) - set key($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasKey() => $_has(0); - @$pb.TagNumber(1) - void clearKey() => clearField(1); - - @$pb.TagNumber(2) - $core.String get value => $_getSZ(1); - @$pb.TagNumber(2) - set value($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasValue() => $_has(1); - @$pb.TagNumber(2) - void clearValue() => clearField(2); -} - -class TimeRange extends $pb.GeneratedMessage { - factory TimeRange() => create(); - TimeRange._() : super(); - factory TimeRange.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory TimeRange.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'TimeRange', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOM<$0.Timestamp>(1, _omitFieldNames ? '' : 'startTime', subBuilder: $0.Timestamp.create) - ..aOM<$0.Timestamp>(2, _omitFieldNames ? '' : 'endTime', subBuilder: $0.Timestamp.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - TimeRange clone() => TimeRange()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - TimeRange copyWith(void Function(TimeRange) updates) => - super.copyWith((message) => updates(message as TimeRange)) as TimeRange; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static TimeRange create() => TimeRange._(); - TimeRange createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static TimeRange getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static TimeRange? _defaultInstance; - - @$pb.TagNumber(1) - $0.Timestamp get startTime => $_getN(0); - @$pb.TagNumber(1) - set startTime($0.Timestamp v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasStartTime() => $_has(0); - @$pb.TagNumber(1) - void clearStartTime() => clearField(1); - @$pb.TagNumber(1) - $0.Timestamp ensureStartTime() => $_ensure(0); - - @$pb.TagNumber(2) - $0.Timestamp get endTime => $_getN(1); - @$pb.TagNumber(2) - set endTime($0.Timestamp v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasEndTime() => $_has(1); - @$pb.TagNumber(2) - void clearEndTime() => clearField(2); - @$pb.TagNumber(2) - $0.Timestamp ensureEndTime() => $_ensure(1); -} - -class RequestedDimension extends $pb.GeneratedMessage { - factory RequestedDimension() => create(); - RequestedDimension._() : super(); - factory RequestedDimension.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory RequestedDimension.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'RequestedDimension', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'key') - ..aOS(2, _omitFieldNames ? '' : 'value') - ..aOM<$1.Duration>(3, _omitFieldNames ? '' : 'expiration', subBuilder: $1.Duration.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - RequestedDimension clone() => RequestedDimension()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - RequestedDimension copyWith(void Function(RequestedDimension) updates) => - super.copyWith((message) => updates(message as RequestedDimension)) as RequestedDimension; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static RequestedDimension create() => RequestedDimension._(); - RequestedDimension createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static RequestedDimension getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static RequestedDimension? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get key => $_getSZ(0); - @$pb.TagNumber(1) - set key($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasKey() => $_has(0); - @$pb.TagNumber(1) - void clearKey() => clearField(1); - - @$pb.TagNumber(2) - $core.String get value => $_getSZ(1); - @$pb.TagNumber(2) - set value($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasValue() => $_has(1); - @$pb.TagNumber(2) - void clearValue() => clearField(2); - - @$pb.TagNumber(3) - $1.Duration get expiration => $_getN(2); - @$pb.TagNumber(3) - set expiration($1.Duration v) { - setField(3, v); - } - - @$pb.TagNumber(3) - $core.bool hasExpiration() => $_has(2); - @$pb.TagNumber(3) - void clearExpiration() => clearField(3); - @$pb.TagNumber(3) - $1.Duration ensureExpiration() => $_ensure(2); -} - -class CacheEntry extends $pb.GeneratedMessage { - factory CacheEntry() => create(); - CacheEntry._() : super(); - factory CacheEntry.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory CacheEntry.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'CacheEntry', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'name') - ..aOS(2, _omitFieldNames ? '' : 'path') - ..aOM<$1.Duration>(3, _omitFieldNames ? '' : 'waitForWarmCache', subBuilder: $1.Duration.create) - ..aOS(4, _omitFieldNames ? '' : 'envVar') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - CacheEntry clone() => CacheEntry()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - CacheEntry copyWith(void Function(CacheEntry) updates) => - super.copyWith((message) => updates(message as CacheEntry)) as CacheEntry; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static CacheEntry create() => CacheEntry._(); - CacheEntry createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static CacheEntry getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static CacheEntry? _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.String get path => $_getSZ(1); - @$pb.TagNumber(2) - set path($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasPath() => $_has(1); - @$pb.TagNumber(2) - void clearPath() => clearField(2); - - @$pb.TagNumber(3) - $1.Duration get waitForWarmCache => $_getN(2); - @$pb.TagNumber(3) - set waitForWarmCache($1.Duration v) { - setField(3, v); - } - - @$pb.TagNumber(3) - $core.bool hasWaitForWarmCache() => $_has(2); - @$pb.TagNumber(3) - void clearWaitForWarmCache() => clearField(3); - @$pb.TagNumber(3) - $1.Duration ensureWaitForWarmCache() => $_ensure(2); - - @$pb.TagNumber(4) - $core.String get envVar => $_getSZ(3); - @$pb.TagNumber(4) - set envVar($core.String v) { - $_setString(3, v); - } - - @$pb.TagNumber(4) - $core.bool hasEnvVar() => $_has(3); - @$pb.TagNumber(4) - void clearEnvVar() => clearField(4); -} - -class HealthStatus extends $pb.GeneratedMessage { - factory HealthStatus() => create(); - HealthStatus._() : super(); - factory HealthStatus.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory HealthStatus.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'HealthStatus', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aInt64(1, _omitFieldNames ? '' : 'healthScore') - ..m<$core.String, $core.double>(2, _omitFieldNames ? '' : 'healthMetrics', - entryClassName: 'HealthStatus.HealthMetricsEntry', - keyFieldType: $pb.PbFieldType.OS, - valueFieldType: $pb.PbFieldType.OF, - packageName: const $pb.PackageName('buildbucket.v2')) - ..aOS(3, _omitFieldNames ? '' : 'description') - ..m<$core.String, $core.String>(4, _omitFieldNames ? '' : 'docLinks', - entryClassName: 'HealthStatus.DocLinksEntry', - keyFieldType: $pb.PbFieldType.OS, - valueFieldType: $pb.PbFieldType.OS, - packageName: const $pb.PackageName('buildbucket.v2')) - ..m<$core.String, $core.String>(5, _omitFieldNames ? '' : 'dataLinks', - entryClassName: 'HealthStatus.DataLinksEntry', - keyFieldType: $pb.PbFieldType.OS, - valueFieldType: $pb.PbFieldType.OS, - packageName: const $pb.PackageName('buildbucket.v2')) - ..aOS(6, _omitFieldNames ? '' : 'reporter') - ..aOM<$0.Timestamp>(7, _omitFieldNames ? '' : 'reportedTime', subBuilder: $0.Timestamp.create) - ..aOS(8, _omitFieldNames ? '' : 'contactTeamEmail') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - HealthStatus clone() => HealthStatus()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - HealthStatus copyWith(void Function(HealthStatus) updates) => - super.copyWith((message) => updates(message as HealthStatus)) as HealthStatus; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static HealthStatus create() => HealthStatus._(); - HealthStatus createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static HealthStatus getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static HealthStatus? _defaultInstance; - - @$pb.TagNumber(1) - $fixnum.Int64 get healthScore => $_getI64(0); - @$pb.TagNumber(1) - set healthScore($fixnum.Int64 v) { - $_setInt64(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasHealthScore() => $_has(0); - @$pb.TagNumber(1) - void clearHealthScore() => clearField(1); - - @$pb.TagNumber(2) - $core.Map<$core.String, $core.double> get healthMetrics => $_getMap(1); - - @$pb.TagNumber(3) - $core.String get description => $_getSZ(2); - @$pb.TagNumber(3) - set description($core.String v) { - $_setString(2, v); - } - - @$pb.TagNumber(3) - $core.bool hasDescription() => $_has(2); - @$pb.TagNumber(3) - void clearDescription() => clearField(3); - - @$pb.TagNumber(4) - $core.Map<$core.String, $core.String> get docLinks => $_getMap(3); - - @$pb.TagNumber(5) - $core.Map<$core.String, $core.String> get dataLinks => $_getMap(4); - - @$pb.TagNumber(6) - $core.String get reporter => $_getSZ(5); - @$pb.TagNumber(6) - set reporter($core.String v) { - $_setString(5, v); - } - - @$pb.TagNumber(6) - $core.bool hasReporter() => $_has(5); - @$pb.TagNumber(6) - void clearReporter() => clearField(6); - - @$pb.TagNumber(7) - $0.Timestamp get reportedTime => $_getN(6); - @$pb.TagNumber(7) - set reportedTime($0.Timestamp v) { - setField(7, v); - } - - @$pb.TagNumber(7) - $core.bool hasReportedTime() => $_has(6); - @$pb.TagNumber(7) - void clearReportedTime() => clearField(7); - @$pb.TagNumber(7) - $0.Timestamp ensureReportedTime() => $_ensure(6); - - @$pb.TagNumber(8) - $core.String get contactTeamEmail => $_getSZ(7); - @$pb.TagNumber(8) - set contactTeamEmail($core.String v) { - $_setString(7, v); - } - - @$pb.TagNumber(8) - $core.bool hasContactTeamEmail() => $_has(7); - @$pb.TagNumber(8) - void clearContactTeamEmail() => clearField(8); -} - -const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); -const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/common.pbenum.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/common.pbenum.dart deleted file mode 100644 index 1bca56a5b..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/common.pbenum.dart +++ /dev/null @@ -1,75 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/common.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:core' as $core; - -import 'package:protobuf/protobuf.dart' as $pb; - -class Status extends $pb.ProtobufEnum { - static const Status STATUS_UNSPECIFIED = Status._(0, _omitEnumNames ? '' : 'STATUS_UNSPECIFIED'); - static const Status SCHEDULED = Status._(1, _omitEnumNames ? '' : 'SCHEDULED'); - static const Status STARTED = Status._(2, _omitEnumNames ? '' : 'STARTED'); - static const Status ENDED_MASK = Status._(4, _omitEnumNames ? '' : 'ENDED_MASK'); - static const Status SUCCESS = Status._(12, _omitEnumNames ? '' : 'SUCCESS'); - static const Status FAILURE = Status._(20, _omitEnumNames ? '' : 'FAILURE'); - static const Status INFRA_FAILURE = Status._(36, _omitEnumNames ? '' : 'INFRA_FAILURE'); - static const Status CANCELED = Status._(68, _omitEnumNames ? '' : 'CANCELED'); - - static const $core.List values = [ - STATUS_UNSPECIFIED, - SCHEDULED, - STARTED, - ENDED_MASK, - SUCCESS, - FAILURE, - INFRA_FAILURE, - CANCELED, - ]; - - static final $core.Map<$core.int, Status> _byValue = $pb.ProtobufEnum.initByValue(values); - static Status? valueOf($core.int value) => _byValue[value]; - - const Status._($core.int v, $core.String n) : super(v, n); -} - -class Trinary extends $pb.ProtobufEnum { - static const Trinary UNSET = Trinary._(0, _omitEnumNames ? '' : 'UNSET'); - static const Trinary YES = Trinary._(1, _omitEnumNames ? '' : 'YES'); - static const Trinary NO = Trinary._(2, _omitEnumNames ? '' : 'NO'); - - static const $core.List values = [ - UNSET, - YES, - NO, - ]; - - static final $core.Map<$core.int, Trinary> _byValue = $pb.ProtobufEnum.initByValue(values); - static Trinary? valueOf($core.int value) => _byValue[value]; - - const Trinary._($core.int v, $core.String n) : super(v, n); -} - -class Compression extends $pb.ProtobufEnum { - static const Compression ZLIB = Compression._(0, _omitEnumNames ? '' : 'ZLIB'); - static const Compression ZSTD = Compression._(1, _omitEnumNames ? '' : 'ZSTD'); - - static const $core.List values = [ - ZLIB, - ZSTD, - ]; - - static final $core.Map<$core.int, Compression> _byValue = $pb.ProtobufEnum.initByValue(values); - static Compression? valueOf($core.int value) => _byValue[value]; - - const Compression._($core.int v, $core.String n) : super(v, n); -} - -const _omitEnumNames = $core.bool.fromEnvironment('protobuf.omit_enum_names'); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/common.pbjson.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/common.pbjson.dart deleted file mode 100644 index cd3b16490..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/common.pbjson.dart +++ /dev/null @@ -1,297 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/common.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:convert' as $convert; -import 'dart:core' as $core; -import 'dart:typed_data' as $typed_data; - -@$core.Deprecated('Use statusDescriptor instead') -const Status$json = { - '1': 'Status', - '2': [ - {'1': 'STATUS_UNSPECIFIED', '2': 0}, - {'1': 'SCHEDULED', '2': 1}, - {'1': 'STARTED', '2': 2}, - {'1': 'ENDED_MASK', '2': 4}, - {'1': 'SUCCESS', '2': 12}, - {'1': 'FAILURE', '2': 20}, - {'1': 'INFRA_FAILURE', '2': 36}, - {'1': 'CANCELED', '2': 68}, - ], -}; - -/// Descriptor for `Status`. Decode as a `google.protobuf.EnumDescriptorProto`. -final $typed_data.Uint8List statusDescriptor = - $convert.base64Decode('CgZTdGF0dXMSFgoSU1RBVFVTX1VOU1BFQ0lGSUVEEAASDQoJU0NIRURVTEVEEAESCwoHU1RBUl' - 'RFRBACEg4KCkVOREVEX01BU0sQBBILCgdTVUNDRVNTEAwSCwoHRkFJTFVSRRAUEhEKDUlORlJB' - 'X0ZBSUxVUkUQJBIMCghDQU5DRUxFRBBE'); - -@$core.Deprecated('Use trinaryDescriptor instead') -const Trinary$json = { - '1': 'Trinary', - '2': [ - {'1': 'UNSET', '2': 0}, - {'1': 'YES', '2': 1}, - {'1': 'NO', '2': 2}, - ], -}; - -/// Descriptor for `Trinary`. Decode as a `google.protobuf.EnumDescriptorProto`. -final $typed_data.Uint8List trinaryDescriptor = - $convert.base64Decode('CgdUcmluYXJ5EgkKBVVOU0VUEAASBwoDWUVTEAESBgoCTk8QAg=='); - -@$core.Deprecated('Use compressionDescriptor instead') -const Compression$json = { - '1': 'Compression', - '2': [ - {'1': 'ZLIB', '2': 0}, - {'1': 'ZSTD', '2': 1}, - ], -}; - -/// Descriptor for `Compression`. Decode as a `google.protobuf.EnumDescriptorProto`. -final $typed_data.Uint8List compressionDescriptor = - $convert.base64Decode('CgtDb21wcmVzc2lvbhIICgRaTElCEAASCAoEWlNURBAB'); - -@$core.Deprecated('Use executableDescriptor instead') -const Executable$json = { - '1': 'Executable', - '2': [ - {'1': 'cipd_package', '3': 1, '4': 1, '5': 9, '10': 'cipdPackage'}, - {'1': 'cipd_version', '3': 2, '4': 1, '5': 9, '10': 'cipdVersion'}, - {'1': 'cmd', '3': 3, '4': 3, '5': 9, '10': 'cmd'}, - {'1': 'wrapper', '3': 4, '4': 3, '5': 9, '10': 'wrapper'}, - ], -}; - -/// Descriptor for `Executable`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List executableDescriptor = - $convert.base64Decode('CgpFeGVjdXRhYmxlEiEKDGNpcGRfcGFja2FnZRgBIAEoCVILY2lwZFBhY2thZ2USIQoMY2lwZF' - '92ZXJzaW9uGAIgASgJUgtjaXBkVmVyc2lvbhIQCgNjbWQYAyADKAlSA2NtZBIYCgd3cmFwcGVy' - 'GAQgAygJUgd3cmFwcGVy'); - -@$core.Deprecated('Use statusDetailsDescriptor instead') -const StatusDetails$json = { - '1': 'StatusDetails', - '2': [ - { - '1': 'resource_exhaustion', - '3': 3, - '4': 1, - '5': 11, - '6': '.buildbucket.v2.StatusDetails.ResourceExhaustion', - '10': 'resourceExhaustion' - }, - {'1': 'timeout', '3': 4, '4': 1, '5': 11, '6': '.buildbucket.v2.StatusDetails.Timeout', '10': 'timeout'}, - ], - '3': [StatusDetails_ResourceExhaustion$json, StatusDetails_Timeout$json], - '9': [ - {'1': 1, '2': 2}, - {'1': 2, '2': 3}, - ], -}; - -@$core.Deprecated('Use statusDetailsDescriptor instead') -const StatusDetails_ResourceExhaustion$json = { - '1': 'ResourceExhaustion', -}; - -@$core.Deprecated('Use statusDetailsDescriptor instead') -const StatusDetails_Timeout$json = { - '1': 'Timeout', -}; - -/// Descriptor for `StatusDetails`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List statusDetailsDescriptor = - $convert.base64Decode('Cg1TdGF0dXNEZXRhaWxzEmEKE3Jlc291cmNlX2V4aGF1c3Rpb24YAyABKAsyMC5idWlsZGJ1Y2' - 'tldC52Mi5TdGF0dXNEZXRhaWxzLlJlc291cmNlRXhoYXVzdGlvblIScmVzb3VyY2VFeGhhdXN0' - 'aW9uEj8KB3RpbWVvdXQYBCABKAsyJS5idWlsZGJ1Y2tldC52Mi5TdGF0dXNEZXRhaWxzLlRpbW' - 'VvdXRSB3RpbWVvdXQaFAoSUmVzb3VyY2VFeGhhdXN0aW9uGgkKB1RpbWVvdXRKBAgBEAJKBAgC' - 'EAM='); - -@$core.Deprecated('Use logDescriptor instead') -const Log$json = { - '1': 'Log', - '2': [ - {'1': 'name', '3': 1, '4': 1, '5': 9, '10': 'name'}, - {'1': 'view_url', '3': 2, '4': 1, '5': 9, '10': 'viewUrl'}, - {'1': 'url', '3': 3, '4': 1, '5': 9, '10': 'url'}, - ], -}; - -/// Descriptor for `Log`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List logDescriptor = - $convert.base64Decode('CgNMb2cSEgoEbmFtZRgBIAEoCVIEbmFtZRIZCgh2aWV3X3VybBgCIAEoCVIHdmlld1VybBIQCg' - 'N1cmwYAyABKAlSA3VybA=='); - -@$core.Deprecated('Use gerritChangeDescriptor instead') -const GerritChange$json = { - '1': 'GerritChange', - '2': [ - {'1': 'host', '3': 1, '4': 1, '5': 9, '10': 'host'}, - {'1': 'project', '3': 2, '4': 1, '5': 9, '10': 'project'}, - {'1': 'change', '3': 3, '4': 1, '5': 3, '10': 'change'}, - {'1': 'patchset', '3': 4, '4': 1, '5': 3, '10': 'patchset'}, - ], -}; - -/// Descriptor for `GerritChange`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List gerritChangeDescriptor = - $convert.base64Decode('CgxHZXJyaXRDaGFuZ2USEgoEaG9zdBgBIAEoCVIEaG9zdBIYCgdwcm9qZWN0GAIgASgJUgdwcm' - '9qZWN0EhYKBmNoYW5nZRgDIAEoA1IGY2hhbmdlEhoKCHBhdGNoc2V0GAQgASgDUghwYXRjaHNl' - 'dA=='); - -@$core.Deprecated('Use gitilesCommitDescriptor instead') -const GitilesCommit$json = { - '1': 'GitilesCommit', - '2': [ - {'1': 'host', '3': 1, '4': 1, '5': 9, '10': 'host'}, - {'1': 'project', '3': 2, '4': 1, '5': 9, '10': 'project'}, - {'1': 'id', '3': 3, '4': 1, '5': 9, '10': 'id'}, - {'1': 'ref', '3': 4, '4': 1, '5': 9, '10': 'ref'}, - {'1': 'position', '3': 5, '4': 1, '5': 13, '10': 'position'}, - ], -}; - -/// Descriptor for `GitilesCommit`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List gitilesCommitDescriptor = - $convert.base64Decode('Cg1HaXRpbGVzQ29tbWl0EhIKBGhvc3QYASABKAlSBGhvc3QSGAoHcHJvamVjdBgCIAEoCVIHcH' - 'JvamVjdBIOCgJpZBgDIAEoCVICaWQSEAoDcmVmGAQgASgJUgNyZWYSGgoIcG9zaXRpb24YBSAB' - 'KA1SCHBvc2l0aW9u'); - -@$core.Deprecated('Use stringPairDescriptor instead') -const StringPair$json = { - '1': 'StringPair', - '2': [ - {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, - {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'}, - ], -}; - -/// Descriptor for `StringPair`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List stringPairDescriptor = - $convert.base64Decode('CgpTdHJpbmdQYWlyEhAKA2tleRgBIAEoCVIDa2V5EhQKBXZhbHVlGAIgASgJUgV2YWx1ZQ=='); - -@$core.Deprecated('Use timeRangeDescriptor instead') -const TimeRange$json = { - '1': 'TimeRange', - '2': [ - {'1': 'start_time', '3': 1, '4': 1, '5': 11, '6': '.google.protobuf.Timestamp', '10': 'startTime'}, - {'1': 'end_time', '3': 2, '4': 1, '5': 11, '6': '.google.protobuf.Timestamp', '10': 'endTime'}, - ], -}; - -/// Descriptor for `TimeRange`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List timeRangeDescriptor = - $convert.base64Decode('CglUaW1lUmFuZ2USOQoKc3RhcnRfdGltZRgBIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3' - 'RhbXBSCXN0YXJ0VGltZRI1CghlbmRfdGltZRgCIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1l' - 'c3RhbXBSB2VuZFRpbWU='); - -@$core.Deprecated('Use requestedDimensionDescriptor instead') -const RequestedDimension$json = { - '1': 'RequestedDimension', - '2': [ - {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, - {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'}, - {'1': 'expiration', '3': 3, '4': 1, '5': 11, '6': '.google.protobuf.Duration', '10': 'expiration'}, - ], -}; - -/// Descriptor for `RequestedDimension`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List requestedDimensionDescriptor = - $convert.base64Decode('ChJSZXF1ZXN0ZWREaW1lbnNpb24SEAoDa2V5GAEgASgJUgNrZXkSFAoFdmFsdWUYAiABKAlSBX' - 'ZhbHVlEjkKCmV4cGlyYXRpb24YAyABKAsyGS5nb29nbGUucHJvdG9idWYuRHVyYXRpb25SCmV4' - 'cGlyYXRpb24='); - -@$core.Deprecated('Use cacheEntryDescriptor instead') -const CacheEntry$json = { - '1': 'CacheEntry', - '2': [ - {'1': 'name', '3': 1, '4': 1, '5': 9, '10': 'name'}, - {'1': 'path', '3': 2, '4': 1, '5': 9, '10': 'path'}, - {'1': 'wait_for_warm_cache', '3': 3, '4': 1, '5': 11, '6': '.google.protobuf.Duration', '10': 'waitForWarmCache'}, - {'1': 'env_var', '3': 4, '4': 1, '5': 9, '10': 'envVar'}, - ], -}; - -/// Descriptor for `CacheEntry`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List cacheEntryDescriptor = - $convert.base64Decode('CgpDYWNoZUVudHJ5EhIKBG5hbWUYASABKAlSBG5hbWUSEgoEcGF0aBgCIAEoCVIEcGF0aBJICh' - 'N3YWl0X2Zvcl93YXJtX2NhY2hlGAMgASgLMhkuZ29vZ2xlLnByb3RvYnVmLkR1cmF0aW9uUhB3' - 'YWl0Rm9yV2FybUNhY2hlEhcKB2Vudl92YXIYBCABKAlSBmVudlZhcg=='); - -@$core.Deprecated('Use healthStatusDescriptor instead') -const HealthStatus$json = { - '1': 'HealthStatus', - '2': [ - {'1': 'health_score', '3': 1, '4': 1, '5': 3, '10': 'healthScore'}, - { - '1': 'health_metrics', - '3': 2, - '4': 3, - '5': 11, - '6': '.buildbucket.v2.HealthStatus.HealthMetricsEntry', - '10': 'healthMetrics' - }, - {'1': 'description', '3': 3, '4': 1, '5': 9, '10': 'description'}, - {'1': 'doc_links', '3': 4, '4': 3, '5': 11, '6': '.buildbucket.v2.HealthStatus.DocLinksEntry', '10': 'docLinks'}, - {'1': 'data_links', '3': 5, '4': 3, '5': 11, '6': '.buildbucket.v2.HealthStatus.DataLinksEntry', '10': 'dataLinks'}, - {'1': 'reporter', '3': 6, '4': 1, '5': 9, '8': {}, '10': 'reporter'}, - {'1': 'reported_time', '3': 7, '4': 1, '5': 11, '6': '.google.protobuf.Timestamp', '8': {}, '10': 'reportedTime'}, - {'1': 'contact_team_email', '3': 8, '4': 1, '5': 9, '10': 'contactTeamEmail'}, - ], - '3': [HealthStatus_HealthMetricsEntry$json, HealthStatus_DocLinksEntry$json, HealthStatus_DataLinksEntry$json], -}; - -@$core.Deprecated('Use healthStatusDescriptor instead') -const HealthStatus_HealthMetricsEntry$json = { - '1': 'HealthMetricsEntry', - '2': [ - {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, - {'1': 'value', '3': 2, '4': 1, '5': 2, '10': 'value'}, - ], - '7': {'7': true}, -}; - -@$core.Deprecated('Use healthStatusDescriptor instead') -const HealthStatus_DocLinksEntry$json = { - '1': 'DocLinksEntry', - '2': [ - {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, - {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'}, - ], - '7': {'7': true}, -}; - -@$core.Deprecated('Use healthStatusDescriptor instead') -const HealthStatus_DataLinksEntry$json = { - '1': 'DataLinksEntry', - '2': [ - {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, - {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'}, - ], - '7': {'7': true}, -}; - -/// Descriptor for `HealthStatus`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List healthStatusDescriptor = - $convert.base64Decode('CgxIZWFsdGhTdGF0dXMSIQoMaGVhbHRoX3Njb3JlGAEgASgDUgtoZWFsdGhTY29yZRJWCg5oZW' - 'FsdGhfbWV0cmljcxgCIAMoCzIvLmJ1aWxkYnVja2V0LnYyLkhlYWx0aFN0YXR1cy5IZWFsdGhN' - 'ZXRyaWNzRW50cnlSDWhlYWx0aE1ldHJpY3MSIAoLZGVzY3JpcHRpb24YAyABKAlSC2Rlc2NyaX' - 'B0aW9uEkcKCWRvY19saW5rcxgEIAMoCzIqLmJ1aWxkYnVja2V0LnYyLkhlYWx0aFN0YXR1cy5E' - 'b2NMaW5rc0VudHJ5Ughkb2NMaW5rcxJKCgpkYXRhX2xpbmtzGAUgAygLMisuYnVpbGRidWNrZX' - 'QudjIuSGVhbHRoU3RhdHVzLkRhdGFMaW5rc0VudHJ5UglkYXRhTGlua3MSHwoIcmVwb3J0ZXIY' - 'BiABKAlCA+BBA1IIcmVwb3J0ZXISRAoNcmVwb3J0ZWRfdGltZRgHIAEoCzIaLmdvb2dsZS5wcm' - '90b2J1Zi5UaW1lc3RhbXBCA+BBA1IMcmVwb3J0ZWRUaW1lEiwKEmNvbnRhY3RfdGVhbV9lbWFp' - 'bBgIIAEoCVIQY29udGFjdFRlYW1FbWFpbBpAChJIZWFsdGhNZXRyaWNzRW50cnkSEAoDa2V5GA' - 'EgASgJUgNrZXkSFAoFdmFsdWUYAiABKAJSBXZhbHVlOgI4ARo7Cg1Eb2NMaW5rc0VudHJ5EhAK' - 'A2tleRgBIAEoCVIDa2V5EhQKBXZhbHVlGAIgASgJUgV2YWx1ZToCOAEaPAoORGF0YUxpbmtzRW' - '50cnkSEAoDa2V5GAEgASgJUgNrZXkSFAoFdmFsdWUYAiABKAlSBXZhbHVlOgI4AQ=='); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/common.pbserver.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/common.pbserver.dart deleted file mode 100644 index fb4c205db..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/common.pbserver.dart +++ /dev/null @@ -1,8 +0,0 @@ -/// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/common.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 'common.pb.dart'; diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/launcher.pb.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/launcher.pb.dart deleted file mode 100644 index 5a7b08a77..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/launcher.pb.dart +++ /dev/null @@ -1,245 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/launcher.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:core' as $core; - -import 'package:protobuf/protobuf.dart' as $pb; - -import 'build.pb.dart' as $0; - -class BuildSecrets extends $pb.GeneratedMessage { - factory BuildSecrets() => create(); - BuildSecrets._() : super(); - factory BuildSecrets.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuildSecrets.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuildSecrets', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'buildToken') - ..aOS(2, _omitFieldNames ? '' : 'resultdbInvocationUpdateToken') - ..aOS(3, _omitFieldNames ? '' : 'startBuildToken') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuildSecrets clone() => BuildSecrets()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuildSecrets copyWith(void Function(BuildSecrets) updates) => - super.copyWith((message) => updates(message as BuildSecrets)) as BuildSecrets; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuildSecrets create() => BuildSecrets._(); - BuildSecrets createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuildSecrets getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuildSecrets? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get buildToken => $_getSZ(0); - @$pb.TagNumber(1) - set buildToken($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasBuildToken() => $_has(0); - @$pb.TagNumber(1) - void clearBuildToken() => clearField(1); - - @$pb.TagNumber(2) - $core.String get resultdbInvocationUpdateToken => $_getSZ(1); - @$pb.TagNumber(2) - set resultdbInvocationUpdateToken($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasResultdbInvocationUpdateToken() => $_has(1); - @$pb.TagNumber(2) - void clearResultdbInvocationUpdateToken() => clearField(2); - - @$pb.TagNumber(3) - $core.String get startBuildToken => $_getSZ(2); - @$pb.TagNumber(3) - set startBuildToken($core.String v) { - $_setString(2, v); - } - - @$pb.TagNumber(3) - $core.bool hasStartBuildToken() => $_has(2); - @$pb.TagNumber(3) - void clearStartBuildToken() => clearField(3); -} - -class BBAgentArgs extends $pb.GeneratedMessage { - factory BBAgentArgs() => create(); - BBAgentArgs._() : super(); - factory BBAgentArgs.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BBAgentArgs.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BBAgentArgs', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'executablePath') - ..aOS(2, _omitFieldNames ? '' : 'cacheDir') - ..pPS(3, _omitFieldNames ? '' : 'knownPublicGerritHosts') - ..aOM<$0.Build>(4, _omitFieldNames ? '' : 'build', subBuilder: $0.Build.create) - ..aOS(5, _omitFieldNames ? '' : 'payloadPath') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BBAgentArgs clone() => BBAgentArgs()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BBAgentArgs copyWith(void Function(BBAgentArgs) updates) => - super.copyWith((message) => updates(message as BBAgentArgs)) as BBAgentArgs; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BBAgentArgs create() => BBAgentArgs._(); - BBAgentArgs createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BBAgentArgs getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BBAgentArgs? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get executablePath => $_getSZ(0); - @$pb.TagNumber(1) - set executablePath($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasExecutablePath() => $_has(0); - @$pb.TagNumber(1) - void clearExecutablePath() => clearField(1); - - @$pb.TagNumber(2) - $core.String get cacheDir => $_getSZ(1); - @$pb.TagNumber(2) - set cacheDir($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasCacheDir() => $_has(1); - @$pb.TagNumber(2) - void clearCacheDir() => clearField(2); - - @$pb.TagNumber(3) - $core.List<$core.String> get knownPublicGerritHosts => $_getList(2); - - @$pb.TagNumber(4) - $0.Build get build => $_getN(3); - @$pb.TagNumber(4) - set build($0.Build v) { - setField(4, v); - } - - @$pb.TagNumber(4) - $core.bool hasBuild() => $_has(3); - @$pb.TagNumber(4) - void clearBuild() => clearField(4); - @$pb.TagNumber(4) - $0.Build ensureBuild() => $_ensure(3); - - @$pb.TagNumber(5) - $core.String get payloadPath => $_getSZ(4); - @$pb.TagNumber(5) - set payloadPath($core.String v) { - $_setString(4, v); - } - - @$pb.TagNumber(5) - $core.bool hasPayloadPath() => $_has(4); - @$pb.TagNumber(5) - void clearPayloadPath() => clearField(5); -} - -class BuildbucketAgentContext extends $pb.GeneratedMessage { - factory BuildbucketAgentContext() => create(); - BuildbucketAgentContext._() : super(); - factory BuildbucketAgentContext.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuildbucketAgentContext.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuildbucketAgentContext', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'taskId') - ..aOM(2, _omitFieldNames ? '' : 'secrets', subBuilder: BuildSecrets.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuildbucketAgentContext clone() => BuildbucketAgentContext()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuildbucketAgentContext copyWith(void Function(BuildbucketAgentContext) updates) => - super.copyWith((message) => updates(message as BuildbucketAgentContext)) as BuildbucketAgentContext; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuildbucketAgentContext create() => BuildbucketAgentContext._(); - BuildbucketAgentContext createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuildbucketAgentContext getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuildbucketAgentContext? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get taskId => $_getSZ(0); - @$pb.TagNumber(1) - set taskId($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasTaskId() => $_has(0); - @$pb.TagNumber(1) - void clearTaskId() => clearField(1); - - @$pb.TagNumber(2) - BuildSecrets get secrets => $_getN(1); - @$pb.TagNumber(2) - set secrets(BuildSecrets v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasSecrets() => $_has(1); - @$pb.TagNumber(2) - void clearSecrets() => clearField(2); - @$pb.TagNumber(2) - BuildSecrets ensureSecrets() => $_ensure(1); -} - -const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); -const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/launcher.pbenum.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/launcher.pbenum.dart deleted file mode 100644 index 8990aa4e6..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/launcher.pbenum.dart +++ /dev/null @@ -1,10 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/launcher.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/launcher.pbjson.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/launcher.pbjson.dart deleted file mode 100644 index 37fa499e3..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/launcher.pbjson.dart +++ /dev/null @@ -1,63 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/launcher.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:convert' as $convert; -import 'dart:core' as $core; -import 'dart:typed_data' as $typed_data; - -@$core.Deprecated('Use buildSecretsDescriptor instead') -const BuildSecrets$json = { - '1': 'BuildSecrets', - '2': [ - {'1': 'build_token', '3': 1, '4': 1, '5': 9, '10': 'buildToken'}, - {'1': 'resultdb_invocation_update_token', '3': 2, '4': 1, '5': 9, '10': 'resultdbInvocationUpdateToken'}, - {'1': 'start_build_token', '3': 3, '4': 1, '5': 9, '10': 'startBuildToken'}, - ], -}; - -/// Descriptor for `BuildSecrets`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List buildSecretsDescriptor = - $convert.base64Decode('CgxCdWlsZFNlY3JldHMSHwoLYnVpbGRfdG9rZW4YASABKAlSCmJ1aWxkVG9rZW4SRwogcmVzdW' - 'x0ZGJfaW52b2NhdGlvbl91cGRhdGVfdG9rZW4YAiABKAlSHXJlc3VsdGRiSW52b2NhdGlvblVw' - 'ZGF0ZVRva2VuEioKEXN0YXJ0X2J1aWxkX3Rva2VuGAMgASgJUg9zdGFydEJ1aWxkVG9rZW4='); - -@$core.Deprecated('Use bBAgentArgsDescriptor instead') -const BBAgentArgs$json = { - '1': 'BBAgentArgs', - '2': [ - {'1': 'executable_path', '3': 1, '4': 1, '5': 9, '10': 'executablePath'}, - {'1': 'payload_path', '3': 5, '4': 1, '5': 9, '10': 'payloadPath'}, - {'1': 'cache_dir', '3': 2, '4': 1, '5': 9, '10': 'cacheDir'}, - {'1': 'known_public_gerrit_hosts', '3': 3, '4': 3, '5': 9, '10': 'knownPublicGerritHosts'}, - {'1': 'build', '3': 4, '4': 1, '5': 11, '6': '.buildbucket.v2.Build', '10': 'build'}, - ], -}; - -/// Descriptor for `BBAgentArgs`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List bBAgentArgsDescriptor = - $convert.base64Decode('CgtCQkFnZW50QXJncxInCg9leGVjdXRhYmxlX3BhdGgYASABKAlSDmV4ZWN1dGFibGVQYXRoEi' - 'EKDHBheWxvYWRfcGF0aBgFIAEoCVILcGF5bG9hZFBhdGgSGwoJY2FjaGVfZGlyGAIgASgJUghj' - 'YWNoZURpchI5Chlrbm93bl9wdWJsaWNfZ2Vycml0X2hvc3RzGAMgAygJUhZrbm93blB1YmxpY0' - 'dlcnJpdEhvc3RzEisKBWJ1aWxkGAQgASgLMhUuYnVpbGRidWNrZXQudjIuQnVpbGRSBWJ1aWxk'); - -@$core.Deprecated('Use buildbucketAgentContextDescriptor instead') -const BuildbucketAgentContext$json = { - '1': 'BuildbucketAgentContext', - '2': [ - {'1': 'task_id', '3': 1, '4': 1, '5': 9, '10': 'taskId'}, - {'1': 'secrets', '3': 2, '4': 1, '5': 11, '6': '.buildbucket.v2.BuildSecrets', '10': 'secrets'}, - ], -}; - -/// Descriptor for `BuildbucketAgentContext`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List buildbucketAgentContextDescriptor = - $convert.base64Decode('ChdCdWlsZGJ1Y2tldEFnZW50Q29udGV4dBIXCgd0YXNrX2lkGAEgASgJUgZ0YXNrSWQSNgoHc2' - 'VjcmV0cxgCIAEoCzIcLmJ1aWxkYnVja2V0LnYyLkJ1aWxkU2VjcmV0c1IHc2VjcmV0cw=='); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/notification.pb.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/notification.pb.dart deleted file mode 100644 index 272abb06f..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/notification.pb.dart +++ /dev/null @@ -1,217 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/notification.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:core' as $core; - -import 'package:protobuf/protobuf.dart' as $pb; - -import 'build.pb.dart' as $0; -import 'common.pbenum.dart' as $1; - -class NotificationConfig extends $pb.GeneratedMessage { - factory NotificationConfig() => create(); - NotificationConfig._() : super(); - factory NotificationConfig.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory NotificationConfig.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'NotificationConfig', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'pubsubTopic') - ..a<$core.List<$core.int>>(2, _omitFieldNames ? '' : 'userData', $pb.PbFieldType.OY) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - NotificationConfig clone() => NotificationConfig()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - NotificationConfig copyWith(void Function(NotificationConfig) updates) => - super.copyWith((message) => updates(message as NotificationConfig)) as NotificationConfig; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static NotificationConfig create() => NotificationConfig._(); - NotificationConfig createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static NotificationConfig getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static NotificationConfig? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get pubsubTopic => $_getSZ(0); - @$pb.TagNumber(1) - set pubsubTopic($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasPubsubTopic() => $_has(0); - @$pb.TagNumber(1) - void clearPubsubTopic() => clearField(1); - - @$pb.TagNumber(2) - $core.List<$core.int> get userData => $_getN(1); - @$pb.TagNumber(2) - set userData($core.List<$core.int> v) { - $_setBytes(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasUserData() => $_has(1); - @$pb.TagNumber(2) - void clearUserData() => clearField(2); -} - -class BuildsV2PubSub extends $pb.GeneratedMessage { - factory BuildsV2PubSub() => create(); - BuildsV2PubSub._() : super(); - factory BuildsV2PubSub.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuildsV2PubSub.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuildsV2PubSub', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOM<$0.Build>(1, _omitFieldNames ? '' : 'build', subBuilder: $0.Build.create) - ..a<$core.List<$core.int>>(2, _omitFieldNames ? '' : 'buildLargeFields', $pb.PbFieldType.OY) - ..e<$1.Compression>(3, _omitFieldNames ? '' : 'compression', $pb.PbFieldType.OE, - defaultOrMaker: $1.Compression.ZLIB, valueOf: $1.Compression.valueOf, enumValues: $1.Compression.values) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuildsV2PubSub clone() => BuildsV2PubSub()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuildsV2PubSub copyWith(void Function(BuildsV2PubSub) updates) => - super.copyWith((message) => updates(message as BuildsV2PubSub)) as BuildsV2PubSub; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuildsV2PubSub create() => BuildsV2PubSub._(); - BuildsV2PubSub createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuildsV2PubSub getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuildsV2PubSub? _defaultInstance; - - @$pb.TagNumber(1) - $0.Build get build => $_getN(0); - @$pb.TagNumber(1) - set build($0.Build v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasBuild() => $_has(0); - @$pb.TagNumber(1) - void clearBuild() => clearField(1); - @$pb.TagNumber(1) - $0.Build ensureBuild() => $_ensure(0); - - @$pb.TagNumber(2) - $core.List<$core.int> get buildLargeFields => $_getN(1); - @$pb.TagNumber(2) - set buildLargeFields($core.List<$core.int> v) { - $_setBytes(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasBuildLargeFields() => $_has(1); - @$pb.TagNumber(2) - void clearBuildLargeFields() => clearField(2); - - @$pb.TagNumber(3) - $1.Compression get compression => $_getN(2); - @$pb.TagNumber(3) - set compression($1.Compression v) { - setField(3, v); - } - - @$pb.TagNumber(3) - $core.bool hasCompression() => $_has(2); - @$pb.TagNumber(3) - void clearCompression() => clearField(3); -} - -class PubSubCallBack extends $pb.GeneratedMessage { - factory PubSubCallBack() => create(); - PubSubCallBack._() : super(); - factory PubSubCallBack.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory PubSubCallBack.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'PubSubCallBack', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOM(1, _omitFieldNames ? '' : 'buildPubsub', subBuilder: BuildsV2PubSub.create) - ..a<$core.List<$core.int>>(2, _omitFieldNames ? '' : 'userData', $pb.PbFieldType.OY) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - PubSubCallBack clone() => PubSubCallBack()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - PubSubCallBack copyWith(void Function(PubSubCallBack) updates) => - super.copyWith((message) => updates(message as PubSubCallBack)) as PubSubCallBack; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static PubSubCallBack create() => PubSubCallBack._(); - PubSubCallBack createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static PubSubCallBack getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static PubSubCallBack? _defaultInstance; - - @$pb.TagNumber(1) - BuildsV2PubSub get buildPubsub => $_getN(0); - @$pb.TagNumber(1) - set buildPubsub(BuildsV2PubSub v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasBuildPubsub() => $_has(0); - @$pb.TagNumber(1) - void clearBuildPubsub() => clearField(1); - @$pb.TagNumber(1) - BuildsV2PubSub ensureBuildPubsub() => $_ensure(0); - - @$pb.TagNumber(2) - $core.List<$core.int> get userData => $_getN(1); - @$pb.TagNumber(2) - set userData($core.List<$core.int> v) { - $_setBytes(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasUserData() => $_has(1); - @$pb.TagNumber(2) - void clearUserData() => clearField(2); -} - -const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); -const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/notification.pbenum.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/notification.pbenum.dart deleted file mode 100644 index 42f8da018..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/notification.pbenum.dart +++ /dev/null @@ -1,10 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/notification.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/notification.pbjson.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/notification.pbjson.dart deleted file mode 100644 index d3c286df7..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/notification.pbjson.dart +++ /dev/null @@ -1,60 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/notification.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:convert' as $convert; -import 'dart:core' as $core; -import 'dart:typed_data' as $typed_data; - -@$core.Deprecated('Use notificationConfigDescriptor instead') -const NotificationConfig$json = { - '1': 'NotificationConfig', - '2': [ - {'1': 'pubsub_topic', '3': 1, '4': 1, '5': 9, '10': 'pubsubTopic'}, - {'1': 'user_data', '3': 2, '4': 1, '5': 12, '10': 'userData'}, - ], -}; - -/// Descriptor for `NotificationConfig`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List notificationConfigDescriptor = - $convert.base64Decode('ChJOb3RpZmljYXRpb25Db25maWcSIQoMcHVic3ViX3RvcGljGAEgASgJUgtwdWJzdWJUb3BpYx' - 'IbCgl1c2VyX2RhdGEYAiABKAxSCHVzZXJEYXRh'); - -@$core.Deprecated('Use buildsV2PubSubDescriptor instead') -const BuildsV2PubSub$json = { - '1': 'BuildsV2PubSub', - '2': [ - {'1': 'build', '3': 1, '4': 1, '5': 11, '6': '.buildbucket.v2.Build', '10': 'build'}, - {'1': 'build_large_fields', '3': 2, '4': 1, '5': 12, '10': 'buildLargeFields'}, - {'1': 'compression', '3': 3, '4': 1, '5': 14, '6': '.buildbucket.v2.Compression', '10': 'compression'}, - ], -}; - -/// Descriptor for `BuildsV2PubSub`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List buildsV2PubSubDescriptor = - $convert.base64Decode('Cg5CdWlsZHNWMlB1YlN1YhIrCgVidWlsZBgBIAEoCzIVLmJ1aWxkYnVja2V0LnYyLkJ1aWxkUg' - 'VidWlsZBIsChJidWlsZF9sYXJnZV9maWVsZHMYAiABKAxSEGJ1aWxkTGFyZ2VGaWVsZHMSPQoL' - 'Y29tcHJlc3Npb24YAyABKA4yGy5idWlsZGJ1Y2tldC52Mi5Db21wcmVzc2lvblILY29tcHJlc3' - 'Npb24='); - -@$core.Deprecated('Use pubSubCallBackDescriptor instead') -const PubSubCallBack$json = { - '1': 'PubSubCallBack', - '2': [ - {'1': 'build_pubsub', '3': 1, '4': 1, '5': 11, '6': '.buildbucket.v2.BuildsV2PubSub', '10': 'buildPubsub'}, - {'1': 'user_data', '3': 2, '4': 1, '5': 12, '10': 'userData'}, - ], -}; - -/// Descriptor for `PubSubCallBack`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List pubSubCallBackDescriptor = - $convert.base64Decode('Cg5QdWJTdWJDYWxsQmFjaxJBCgxidWlsZF9wdWJzdWIYASABKAsyHi5idWlsZGJ1Y2tldC52Mi' - '5CdWlsZHNWMlB1YlN1YlILYnVpbGRQdWJzdWISGwoJdXNlcl9kYXRhGAIgASgMUgh1c2VyRGF0' - 'YQ=='); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/project_config.pb.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/project_config.pb.dart deleted file mode 100644 index 78d28b0ea..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/project_config.pb.dart +++ /dev/null @@ -1,1348 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/project_config.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:core' as $core; - -import 'package:protobuf/protobuf.dart' as $pb; - -import '../../../../google/protobuf/duration.pb.dart' as $2; -import '../../../../google/protobuf/wrappers.pb.dart' as $0; -import '../../resultdb/proto/v1/invocation.pb.dart' as $3; -import 'common.pb.dart' as $1; -import 'common.pbenum.dart' as $1; -import 'project_config.pbenum.dart'; - -export 'project_config.pbenum.dart'; - -class Acl extends $pb.GeneratedMessage { - factory Acl() => create(); - Acl._() : super(); - factory Acl.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory Acl.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Acl', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket'), createEmptyInstance: create) - ..e(1, _omitFieldNames ? '' : 'role', $pb.PbFieldType.OE, - defaultOrMaker: Acl_Role.READER, valueOf: Acl_Role.valueOf, enumValues: Acl_Role.values) - ..aOS(2, _omitFieldNames ? '' : 'group') - ..aOS(3, _omitFieldNames ? '' : 'identity') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - Acl clone() => Acl()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - Acl copyWith(void Function(Acl) updates) => super.copyWith((message) => updates(message as Acl)) as Acl; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static Acl create() => Acl._(); - Acl createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Acl getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Acl? _defaultInstance; - - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(1) - Acl_Role get role => $_getN(0); - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(1) - set role(Acl_Role v) { - setField(1, v); - } - - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(1) - $core.bool hasRole() => $_has(0); - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(1) - void clearRole() => clearField(1); - - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(2) - $core.String get group => $_getSZ(1); - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(2) - set group($core.String v) { - $_setString(1, v); - } - - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(2) - $core.bool hasGroup() => $_has(1); - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(2) - void clearGroup() => clearField(2); - - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(3) - $core.String get identity => $_getSZ(2); - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(3) - set identity($core.String v) { - $_setString(2, v); - } - - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(3) - $core.bool hasIdentity() => $_has(2); - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(3) - void clearIdentity() => clearField(3); -} - -class BuilderConfig_CacheEntry extends $pb.GeneratedMessage { - factory BuilderConfig_CacheEntry() => create(); - BuilderConfig_CacheEntry._() : super(); - factory BuilderConfig_CacheEntry.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuilderConfig_CacheEntry.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuilderConfig.CacheEntry', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'name') - ..aOS(2, _omitFieldNames ? '' : 'path') - ..a<$core.int>(3, _omitFieldNames ? '' : 'waitForWarmCacheSecs', $pb.PbFieldType.O3) - ..aOS(4, _omitFieldNames ? '' : 'envVar') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuilderConfig_CacheEntry clone() => BuilderConfig_CacheEntry()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuilderConfig_CacheEntry copyWith(void Function(BuilderConfig_CacheEntry) updates) => - super.copyWith((message) => updates(message as BuilderConfig_CacheEntry)) as BuilderConfig_CacheEntry; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuilderConfig_CacheEntry create() => BuilderConfig_CacheEntry._(); - BuilderConfig_CacheEntry createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuilderConfig_CacheEntry getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuilderConfig_CacheEntry? _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.String get path => $_getSZ(1); - @$pb.TagNumber(2) - set path($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasPath() => $_has(1); - @$pb.TagNumber(2) - void clearPath() => clearField(2); - - @$pb.TagNumber(3) - $core.int get waitForWarmCacheSecs => $_getIZ(2); - @$pb.TagNumber(3) - set waitForWarmCacheSecs($core.int v) { - $_setSignedInt32(2, v); - } - - @$pb.TagNumber(3) - $core.bool hasWaitForWarmCacheSecs() => $_has(2); - @$pb.TagNumber(3) - void clearWaitForWarmCacheSecs() => clearField(3); - - @$pb.TagNumber(4) - $core.String get envVar => $_getSZ(3); - @$pb.TagNumber(4) - set envVar($core.String v) { - $_setString(3, v); - } - - @$pb.TagNumber(4) - $core.bool hasEnvVar() => $_has(3); - @$pb.TagNumber(4) - void clearEnvVar() => clearField(4); -} - -class BuilderConfig_Recipe extends $pb.GeneratedMessage { - factory BuilderConfig_Recipe() => create(); - BuilderConfig_Recipe._() : super(); - factory BuilderConfig_Recipe.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuilderConfig_Recipe.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuilderConfig.Recipe', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket'), createEmptyInstance: create) - ..aOS(2, _omitFieldNames ? '' : 'name') - ..pPS(3, _omitFieldNames ? '' : 'properties') - ..pPS(4, _omitFieldNames ? '' : 'propertiesJ') - ..aOS(5, _omitFieldNames ? '' : 'cipdVersion') - ..aOS(6, _omitFieldNames ? '' : 'cipdPackage') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuilderConfig_Recipe clone() => BuilderConfig_Recipe()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuilderConfig_Recipe copyWith(void Function(BuilderConfig_Recipe) updates) => - super.copyWith((message) => updates(message as BuilderConfig_Recipe)) as BuilderConfig_Recipe; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuilderConfig_Recipe create() => BuilderConfig_Recipe._(); - BuilderConfig_Recipe createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuilderConfig_Recipe getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuilderConfig_Recipe? _defaultInstance; - - @$pb.TagNumber(2) - $core.String get name => $_getSZ(0); - @$pb.TagNumber(2) - set name($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(2) - $core.bool hasName() => $_has(0); - @$pb.TagNumber(2) - void clearName() => clearField(2); - - @$pb.TagNumber(3) - $core.List<$core.String> get properties => $_getList(1); - - @$pb.TagNumber(4) - $core.List<$core.String> get propertiesJ => $_getList(2); - - @$pb.TagNumber(5) - $core.String get cipdVersion => $_getSZ(3); - @$pb.TagNumber(5) - set cipdVersion($core.String v) { - $_setString(3, v); - } - - @$pb.TagNumber(5) - $core.bool hasCipdVersion() => $_has(3); - @$pb.TagNumber(5) - void clearCipdVersion() => clearField(5); - - @$pb.TagNumber(6) - $core.String get cipdPackage => $_getSZ(4); - @$pb.TagNumber(6) - set cipdPackage($core.String v) { - $_setString(4, v); - } - - @$pb.TagNumber(6) - $core.bool hasCipdPackage() => $_has(4); - @$pb.TagNumber(6) - void clearCipdPackage() => clearField(6); -} - -class BuilderConfig_ResultDB extends $pb.GeneratedMessage { - factory BuilderConfig_ResultDB() => create(); - BuilderConfig_ResultDB._() : super(); - factory BuilderConfig_ResultDB.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuilderConfig_ResultDB.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuilderConfig.ResultDB', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket'), createEmptyInstance: create) - ..aOB(1, _omitFieldNames ? '' : 'enable') - ..pc<$3.BigQueryExport>(2, _omitFieldNames ? '' : 'bqExports', $pb.PbFieldType.PM, - subBuilder: $3.BigQueryExport.create) - ..aOM<$3.HistoryOptions>(3, _omitFieldNames ? '' : 'historyOptions', subBuilder: $3.HistoryOptions.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuilderConfig_ResultDB clone() => BuilderConfig_ResultDB()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuilderConfig_ResultDB copyWith(void Function(BuilderConfig_ResultDB) updates) => - super.copyWith((message) => updates(message as BuilderConfig_ResultDB)) as BuilderConfig_ResultDB; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuilderConfig_ResultDB create() => BuilderConfig_ResultDB._(); - BuilderConfig_ResultDB createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuilderConfig_ResultDB getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuilderConfig_ResultDB? _defaultInstance; - - @$pb.TagNumber(1) - $core.bool get enable => $_getBF(0); - @$pb.TagNumber(1) - set enable($core.bool v) { - $_setBool(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasEnable() => $_has(0); - @$pb.TagNumber(1) - void clearEnable() => clearField(1); - - @$pb.TagNumber(2) - $core.List<$3.BigQueryExport> get bqExports => $_getList(1); - - @$pb.TagNumber(3) - $3.HistoryOptions get historyOptions => $_getN(2); - @$pb.TagNumber(3) - set historyOptions($3.HistoryOptions v) { - setField(3, v); - } - - @$pb.TagNumber(3) - $core.bool hasHistoryOptions() => $_has(2); - @$pb.TagNumber(3) - void clearHistoryOptions() => clearField(3); - @$pb.TagNumber(3) - $3.HistoryOptions ensureHistoryOptions() => $_ensure(2); -} - -class BuilderConfig_Backend extends $pb.GeneratedMessage { - factory BuilderConfig_Backend() => create(); - BuilderConfig_Backend._() : super(); - factory BuilderConfig_Backend.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuilderConfig_Backend.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuilderConfig.Backend', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'target') - ..aOS(2, _omitFieldNames ? '' : 'configJson') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuilderConfig_Backend clone() => BuilderConfig_Backend()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuilderConfig_Backend copyWith(void Function(BuilderConfig_Backend) updates) => - super.copyWith((message) => updates(message as BuilderConfig_Backend)) as BuilderConfig_Backend; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuilderConfig_Backend create() => BuilderConfig_Backend._(); - BuilderConfig_Backend createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuilderConfig_Backend getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuilderConfig_Backend? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get target => $_getSZ(0); - @$pb.TagNumber(1) - set target($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasTarget() => $_has(0); - @$pb.TagNumber(1) - void clearTarget() => clearField(1); - - @$pb.TagNumber(2) - $core.String get configJson => $_getSZ(1); - @$pb.TagNumber(2) - set configJson($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasConfigJson() => $_has(1); - @$pb.TagNumber(2) - void clearConfigJson() => clearField(2); -} - -class BuilderConfig_ShadowBuilderAdjustments extends $pb.GeneratedMessage { - factory BuilderConfig_ShadowBuilderAdjustments() => create(); - BuilderConfig_ShadowBuilderAdjustments._() : super(); - factory BuilderConfig_ShadowBuilderAdjustments.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuilderConfig_ShadowBuilderAdjustments.fromJson($core.String i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuilderConfig.ShadowBuilderAdjustments', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'serviceAccount') - ..aOS(2, _omitFieldNames ? '' : 'pool') - ..aOS(3, _omitFieldNames ? '' : 'properties') - ..pPS(4, _omitFieldNames ? '' : 'dimensions') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuilderConfig_ShadowBuilderAdjustments clone() => BuilderConfig_ShadowBuilderAdjustments()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuilderConfig_ShadowBuilderAdjustments copyWith(void Function(BuilderConfig_ShadowBuilderAdjustments) updates) => - super.copyWith((message) => updates(message as BuilderConfig_ShadowBuilderAdjustments)) - as BuilderConfig_ShadowBuilderAdjustments; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuilderConfig_ShadowBuilderAdjustments create() => BuilderConfig_ShadowBuilderAdjustments._(); - BuilderConfig_ShadowBuilderAdjustments createEmptyInstance() => create(); - static $pb.PbList createRepeated() => - $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuilderConfig_ShadowBuilderAdjustments getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuilderConfig_ShadowBuilderAdjustments? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get serviceAccount => $_getSZ(0); - @$pb.TagNumber(1) - set serviceAccount($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasServiceAccount() => $_has(0); - @$pb.TagNumber(1) - void clearServiceAccount() => clearField(1); - - @$pb.TagNumber(2) - $core.String get pool => $_getSZ(1); - @$pb.TagNumber(2) - set pool($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasPool() => $_has(1); - @$pb.TagNumber(2) - void clearPool() => clearField(2); - - @$pb.TagNumber(3) - $core.String get properties => $_getSZ(2); - @$pb.TagNumber(3) - set properties($core.String v) { - $_setString(2, v); - } - - @$pb.TagNumber(3) - $core.bool hasProperties() => $_has(2); - @$pb.TagNumber(3) - void clearProperties() => clearField(3); - - @$pb.TagNumber(4) - $core.List<$core.String> get dimensions => $_getList(3); -} - -class BuilderConfig_BuilderHealthLinks extends $pb.GeneratedMessage { - factory BuilderConfig_BuilderHealthLinks() => create(); - BuilderConfig_BuilderHealthLinks._() : super(); - factory BuilderConfig_BuilderHealthLinks.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuilderConfig_BuilderHealthLinks.fromJson($core.String i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuilderConfig.BuilderHealthLinks', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket'), createEmptyInstance: create) - ..m<$core.String, $core.String>(1, _omitFieldNames ? '' : 'docLinks', - entryClassName: 'BuilderConfig.BuilderHealthLinks.DocLinksEntry', - keyFieldType: $pb.PbFieldType.OS, - valueFieldType: $pb.PbFieldType.OS, - packageName: const $pb.PackageName('buildbucket')) - ..m<$core.String, $core.String>(2, _omitFieldNames ? '' : 'dataLinks', - entryClassName: 'BuilderConfig.BuilderHealthLinks.DataLinksEntry', - keyFieldType: $pb.PbFieldType.OS, - valueFieldType: $pb.PbFieldType.OS, - packageName: const $pb.PackageName('buildbucket')) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuilderConfig_BuilderHealthLinks clone() => BuilderConfig_BuilderHealthLinks()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuilderConfig_BuilderHealthLinks copyWith(void Function(BuilderConfig_BuilderHealthLinks) updates) => - super.copyWith((message) => updates(message as BuilderConfig_BuilderHealthLinks)) - as BuilderConfig_BuilderHealthLinks; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuilderConfig_BuilderHealthLinks create() => BuilderConfig_BuilderHealthLinks._(); - BuilderConfig_BuilderHealthLinks createEmptyInstance() => create(); - static $pb.PbList createRepeated() => - $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuilderConfig_BuilderHealthLinks getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuilderConfig_BuilderHealthLinks? _defaultInstance; - - @$pb.TagNumber(1) - $core.Map<$core.String, $core.String> get docLinks => $_getMap(0); - - @$pb.TagNumber(2) - $core.Map<$core.String, $core.String> get dataLinks => $_getMap(1); -} - -class BuilderConfig extends $pb.GeneratedMessage { - factory BuilderConfig() => create(); - BuilderConfig._() : super(); - factory BuilderConfig.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuilderConfig.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuilderConfig', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'name') - ..pPS(2, _omitFieldNames ? '' : 'swarmingTags') - ..pPS(3, _omitFieldNames ? '' : 'dimensions') - ..aOM(4, _omitFieldNames ? '' : 'recipe', subBuilder: BuilderConfig_Recipe.create) - ..a<$core.int>(5, _omitFieldNames ? '' : 'priority', $pb.PbFieldType.OU3) - ..aOS(6, _omitFieldNames ? '' : 'category') - ..a<$core.int>(7, _omitFieldNames ? '' : 'executionTimeoutSecs', $pb.PbFieldType.OU3) - ..pc(9, _omitFieldNames ? '' : 'caches', $pb.PbFieldType.PM, - subBuilder: BuilderConfig_CacheEntry.create) - ..aOS(12, _omitFieldNames ? '' : 'serviceAccount') - ..e(16, _omitFieldNames ? '' : 'buildNumbers', $pb.PbFieldType.OE, - defaultOrMaker: Toggle.UNSET, valueOf: Toggle.valueOf, enumValues: Toggle.values) - ..e(17, _omitFieldNames ? '' : 'autoBuilderDimension', $pb.PbFieldType.OE, - defaultOrMaker: Toggle.UNSET, valueOf: Toggle.valueOf, enumValues: Toggle.values) - ..e(18, _omitFieldNames ? '' : 'experimental', $pb.PbFieldType.OE, - defaultOrMaker: Toggle.UNSET, valueOf: Toggle.valueOf, enumValues: Toggle.values) - ..a<$core.int>(20, _omitFieldNames ? '' : 'expirationSecs', $pb.PbFieldType.OU3) - ..aOS(21, _omitFieldNames ? '' : 'swarmingHost') - ..aOM<$0.UInt32Value>(22, _omitFieldNames ? '' : 'taskTemplateCanaryPercentage', subBuilder: $0.UInt32Value.create) - ..aOM<$1.Executable>(23, _omitFieldNames ? '' : 'exe', subBuilder: $1.Executable.create) - ..aOS(24, _omitFieldNames ? '' : 'properties') - ..e<$1.Trinary>(25, _omitFieldNames ? '' : 'critical', $pb.PbFieldType.OE, - defaultOrMaker: $1.Trinary.UNSET, valueOf: $1.Trinary.valueOf, enumValues: $1.Trinary.values) - ..aOM(26, _omitFieldNames ? '' : 'resultdb', subBuilder: BuilderConfig_ResultDB.create) - ..m<$core.String, $core.int>(28, _omitFieldNames ? '' : 'experiments', - entryClassName: 'BuilderConfig.ExperimentsEntry', - keyFieldType: $pb.PbFieldType.OS, - valueFieldType: $pb.PbFieldType.O3, - packageName: const $pb.PackageName('buildbucket')) - ..e<$1.Trinary>(29, _omitFieldNames ? '' : 'waitForCapacity', $pb.PbFieldType.OE, - defaultOrMaker: $1.Trinary.UNSET, valueOf: $1.Trinary.valueOf, enumValues: $1.Trinary.values) - ..aOS(30, _omitFieldNames ? '' : 'descriptionHtml') - ..aOM<$2.Duration>(31, _omitFieldNames ? '' : 'gracePeriod', subBuilder: $2.Duration.create) - ..aOM(32, _omitFieldNames ? '' : 'backend', subBuilder: BuilderConfig_Backend.create) - ..aOM(33, _omitFieldNames ? '' : 'backendAlt', subBuilder: BuilderConfig_Backend.create) - ..pPS(34, _omitFieldNames ? '' : 'allowedPropertyOverrides') - ..aOM(35, _omitFieldNames ? '' : 'shadowBuilderAdjustments', - subBuilder: BuilderConfig_ShadowBuilderAdjustments.create) - ..e<$1.Trinary>(36, _omitFieldNames ? '' : 'retriable', $pb.PbFieldType.OE, - defaultOrMaker: $1.Trinary.UNSET, valueOf: $1.Trinary.valueOf, enumValues: $1.Trinary.values) - ..aOM(37, _omitFieldNames ? '' : 'builderHealthMetricsLinks', - subBuilder: BuilderConfig_BuilderHealthLinks.create) - ..aOS(38, _omitFieldNames ? '' : 'contactTeamEmail') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuilderConfig clone() => BuilderConfig()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuilderConfig copyWith(void Function(BuilderConfig) updates) => - super.copyWith((message) => updates(message as BuilderConfig)) as BuilderConfig; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuilderConfig create() => BuilderConfig._(); - BuilderConfig createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuilderConfig getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuilderConfig? _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 swarmingTags => $_getList(1); - - @$pb.TagNumber(3) - $core.List<$core.String> get dimensions => $_getList(2); - - @$pb.TagNumber(4) - BuilderConfig_Recipe get recipe => $_getN(3); - @$pb.TagNumber(4) - set recipe(BuilderConfig_Recipe v) { - setField(4, v); - } - - @$pb.TagNumber(4) - $core.bool hasRecipe() => $_has(3); - @$pb.TagNumber(4) - void clearRecipe() => clearField(4); - @$pb.TagNumber(4) - BuilderConfig_Recipe ensureRecipe() => $_ensure(3); - - @$pb.TagNumber(5) - $core.int get priority => $_getIZ(4); - @$pb.TagNumber(5) - set priority($core.int v) { - $_setUnsignedInt32(4, v); - } - - @$pb.TagNumber(5) - $core.bool hasPriority() => $_has(4); - @$pb.TagNumber(5) - void clearPriority() => clearField(5); - - @$pb.TagNumber(6) - $core.String get category => $_getSZ(5); - @$pb.TagNumber(6) - set category($core.String v) { - $_setString(5, v); - } - - @$pb.TagNumber(6) - $core.bool hasCategory() => $_has(5); - @$pb.TagNumber(6) - void clearCategory() => clearField(6); - - @$pb.TagNumber(7) - $core.int get executionTimeoutSecs => $_getIZ(6); - @$pb.TagNumber(7) - set executionTimeoutSecs($core.int v) { - $_setUnsignedInt32(6, v); - } - - @$pb.TagNumber(7) - $core.bool hasExecutionTimeoutSecs() => $_has(6); - @$pb.TagNumber(7) - void clearExecutionTimeoutSecs() => clearField(7); - - @$pb.TagNumber(9) - $core.List get caches => $_getList(7); - - @$pb.TagNumber(12) - $core.String get serviceAccount => $_getSZ(8); - @$pb.TagNumber(12) - set serviceAccount($core.String v) { - $_setString(8, v); - } - - @$pb.TagNumber(12) - $core.bool hasServiceAccount() => $_has(8); - @$pb.TagNumber(12) - void clearServiceAccount() => clearField(12); - - @$pb.TagNumber(16) - Toggle get buildNumbers => $_getN(9); - @$pb.TagNumber(16) - set buildNumbers(Toggle v) { - setField(16, v); - } - - @$pb.TagNumber(16) - $core.bool hasBuildNumbers() => $_has(9); - @$pb.TagNumber(16) - void clearBuildNumbers() => clearField(16); - - @$pb.TagNumber(17) - Toggle get autoBuilderDimension => $_getN(10); - @$pb.TagNumber(17) - set autoBuilderDimension(Toggle v) { - setField(17, v); - } - - @$pb.TagNumber(17) - $core.bool hasAutoBuilderDimension() => $_has(10); - @$pb.TagNumber(17) - void clearAutoBuilderDimension() => clearField(17); - - @$pb.TagNumber(18) - Toggle get experimental => $_getN(11); - @$pb.TagNumber(18) - set experimental(Toggle v) { - setField(18, v); - } - - @$pb.TagNumber(18) - $core.bool hasExperimental() => $_has(11); - @$pb.TagNumber(18) - void clearExperimental() => clearField(18); - - @$pb.TagNumber(20) - $core.int get expirationSecs => $_getIZ(12); - @$pb.TagNumber(20) - set expirationSecs($core.int v) { - $_setUnsignedInt32(12, v); - } - - @$pb.TagNumber(20) - $core.bool hasExpirationSecs() => $_has(12); - @$pb.TagNumber(20) - void clearExpirationSecs() => clearField(20); - - @$pb.TagNumber(21) - $core.String get swarmingHost => $_getSZ(13); - @$pb.TagNumber(21) - set swarmingHost($core.String v) { - $_setString(13, v); - } - - @$pb.TagNumber(21) - $core.bool hasSwarmingHost() => $_has(13); - @$pb.TagNumber(21) - void clearSwarmingHost() => clearField(21); - - @$pb.TagNumber(22) - $0.UInt32Value get taskTemplateCanaryPercentage => $_getN(14); - @$pb.TagNumber(22) - set taskTemplateCanaryPercentage($0.UInt32Value v) { - setField(22, v); - } - - @$pb.TagNumber(22) - $core.bool hasTaskTemplateCanaryPercentage() => $_has(14); - @$pb.TagNumber(22) - void clearTaskTemplateCanaryPercentage() => clearField(22); - @$pb.TagNumber(22) - $0.UInt32Value ensureTaskTemplateCanaryPercentage() => $_ensure(14); - - @$pb.TagNumber(23) - $1.Executable get exe => $_getN(15); - @$pb.TagNumber(23) - set exe($1.Executable v) { - setField(23, v); - } - - @$pb.TagNumber(23) - $core.bool hasExe() => $_has(15); - @$pb.TagNumber(23) - void clearExe() => clearField(23); - @$pb.TagNumber(23) - $1.Executable ensureExe() => $_ensure(15); - - @$pb.TagNumber(24) - $core.String get properties => $_getSZ(16); - @$pb.TagNumber(24) - set properties($core.String v) { - $_setString(16, v); - } - - @$pb.TagNumber(24) - $core.bool hasProperties() => $_has(16); - @$pb.TagNumber(24) - void clearProperties() => clearField(24); - - @$pb.TagNumber(25) - $1.Trinary get critical => $_getN(17); - @$pb.TagNumber(25) - set critical($1.Trinary v) { - setField(25, v); - } - - @$pb.TagNumber(25) - $core.bool hasCritical() => $_has(17); - @$pb.TagNumber(25) - void clearCritical() => clearField(25); - - @$pb.TagNumber(26) - BuilderConfig_ResultDB get resultdb => $_getN(18); - @$pb.TagNumber(26) - set resultdb(BuilderConfig_ResultDB v) { - setField(26, v); - } - - @$pb.TagNumber(26) - $core.bool hasResultdb() => $_has(18); - @$pb.TagNumber(26) - void clearResultdb() => clearField(26); - @$pb.TagNumber(26) - BuilderConfig_ResultDB ensureResultdb() => $_ensure(18); - - @$pb.TagNumber(28) - $core.Map<$core.String, $core.int> get experiments => $_getMap(19); - - @$pb.TagNumber(29) - $1.Trinary get waitForCapacity => $_getN(20); - @$pb.TagNumber(29) - set waitForCapacity($1.Trinary v) { - setField(29, v); - } - - @$pb.TagNumber(29) - $core.bool hasWaitForCapacity() => $_has(20); - @$pb.TagNumber(29) - void clearWaitForCapacity() => clearField(29); - - @$pb.TagNumber(30) - $core.String get descriptionHtml => $_getSZ(21); - @$pb.TagNumber(30) - set descriptionHtml($core.String v) { - $_setString(21, v); - } - - @$pb.TagNumber(30) - $core.bool hasDescriptionHtml() => $_has(21); - @$pb.TagNumber(30) - void clearDescriptionHtml() => clearField(30); - - @$pb.TagNumber(31) - $2.Duration get gracePeriod => $_getN(22); - @$pb.TagNumber(31) - set gracePeriod($2.Duration v) { - setField(31, v); - } - - @$pb.TagNumber(31) - $core.bool hasGracePeriod() => $_has(22); - @$pb.TagNumber(31) - void clearGracePeriod() => clearField(31); - @$pb.TagNumber(31) - $2.Duration ensureGracePeriod() => $_ensure(22); - - @$pb.TagNumber(32) - BuilderConfig_Backend get backend => $_getN(23); - @$pb.TagNumber(32) - set backend(BuilderConfig_Backend v) { - setField(32, v); - } - - @$pb.TagNumber(32) - $core.bool hasBackend() => $_has(23); - @$pb.TagNumber(32) - void clearBackend() => clearField(32); - @$pb.TagNumber(32) - BuilderConfig_Backend ensureBackend() => $_ensure(23); - - @$pb.TagNumber(33) - BuilderConfig_Backend get backendAlt => $_getN(24); - @$pb.TagNumber(33) - set backendAlt(BuilderConfig_Backend v) { - setField(33, v); - } - - @$pb.TagNumber(33) - $core.bool hasBackendAlt() => $_has(24); - @$pb.TagNumber(33) - void clearBackendAlt() => clearField(33); - @$pb.TagNumber(33) - BuilderConfig_Backend ensureBackendAlt() => $_ensure(24); - - @$pb.TagNumber(34) - $core.List<$core.String> get allowedPropertyOverrides => $_getList(25); - - @$pb.TagNumber(35) - BuilderConfig_ShadowBuilderAdjustments get shadowBuilderAdjustments => $_getN(26); - @$pb.TagNumber(35) - set shadowBuilderAdjustments(BuilderConfig_ShadowBuilderAdjustments v) { - setField(35, v); - } - - @$pb.TagNumber(35) - $core.bool hasShadowBuilderAdjustments() => $_has(26); - @$pb.TagNumber(35) - void clearShadowBuilderAdjustments() => clearField(35); - @$pb.TagNumber(35) - BuilderConfig_ShadowBuilderAdjustments ensureShadowBuilderAdjustments() => $_ensure(26); - - @$pb.TagNumber(36) - $1.Trinary get retriable => $_getN(27); - @$pb.TagNumber(36) - set retriable($1.Trinary v) { - setField(36, v); - } - - @$pb.TagNumber(36) - $core.bool hasRetriable() => $_has(27); - @$pb.TagNumber(36) - void clearRetriable() => clearField(36); - - @$pb.TagNumber(37) - BuilderConfig_BuilderHealthLinks get builderHealthMetricsLinks => $_getN(28); - @$pb.TagNumber(37) - set builderHealthMetricsLinks(BuilderConfig_BuilderHealthLinks v) { - setField(37, v); - } - - @$pb.TagNumber(37) - $core.bool hasBuilderHealthMetricsLinks() => $_has(28); - @$pb.TagNumber(37) - void clearBuilderHealthMetricsLinks() => clearField(37); - @$pb.TagNumber(37) - BuilderConfig_BuilderHealthLinks ensureBuilderHealthMetricsLinks() => $_ensure(28); - - @$pb.TagNumber(38) - $core.String get contactTeamEmail => $_getSZ(29); - @$pb.TagNumber(38) - set contactTeamEmail($core.String v) { - $_setString(29, v); - } - - @$pb.TagNumber(38) - $core.bool hasContactTeamEmail() => $_has(29); - @$pb.TagNumber(38) - void clearContactTeamEmail() => clearField(38); -} - -class Swarming extends $pb.GeneratedMessage { - factory Swarming() => create(); - Swarming._() : super(); - factory Swarming.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory Swarming.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Swarming', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket'), createEmptyInstance: create) - ..pc(4, _omitFieldNames ? '' : 'builders', $pb.PbFieldType.PM, subBuilder: BuilderConfig.create) - ..aOM<$0.UInt32Value>(5, _omitFieldNames ? '' : 'taskTemplateCanaryPercentage', subBuilder: $0.UInt32Value.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - Swarming clone() => Swarming()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - Swarming copyWith(void Function(Swarming) updates) => - super.copyWith((message) => updates(message as Swarming)) as Swarming; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static Swarming create() => Swarming._(); - Swarming createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Swarming getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Swarming? _defaultInstance; - - @$pb.TagNumber(4) - $core.List get builders => $_getList(0); - - @$pb.TagNumber(5) - $0.UInt32Value get taskTemplateCanaryPercentage => $_getN(1); - @$pb.TagNumber(5) - set taskTemplateCanaryPercentage($0.UInt32Value v) { - setField(5, v); - } - - @$pb.TagNumber(5) - $core.bool hasTaskTemplateCanaryPercentage() => $_has(1); - @$pb.TagNumber(5) - void clearTaskTemplateCanaryPercentage() => clearField(5); - @$pb.TagNumber(5) - $0.UInt32Value ensureTaskTemplateCanaryPercentage() => $_ensure(1); -} - -class Bucket_Constraints extends $pb.GeneratedMessage { - factory Bucket_Constraints() => create(); - Bucket_Constraints._() : super(); - factory Bucket_Constraints.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory Bucket_Constraints.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Bucket.Constraints', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket'), createEmptyInstance: create) - ..pPS(1, _omitFieldNames ? '' : 'pools') - ..pPS(2, _omitFieldNames ? '' : 'serviceAccounts') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - Bucket_Constraints clone() => Bucket_Constraints()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - Bucket_Constraints copyWith(void Function(Bucket_Constraints) updates) => - super.copyWith((message) => updates(message as Bucket_Constraints)) as Bucket_Constraints; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static Bucket_Constraints create() => Bucket_Constraints._(); - Bucket_Constraints createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Bucket_Constraints getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Bucket_Constraints? _defaultInstance; - - @$pb.TagNumber(1) - $core.List<$core.String> get pools => $_getList(0); - - @$pb.TagNumber(2) - $core.List<$core.String> get serviceAccounts => $_getList(1); -} - -class Bucket_DynamicBuilderTemplate extends $pb.GeneratedMessage { - factory Bucket_DynamicBuilderTemplate() => create(); - Bucket_DynamicBuilderTemplate._() : super(); - factory Bucket_DynamicBuilderTemplate.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory Bucket_DynamicBuilderTemplate.fromJson($core.String i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Bucket.DynamicBuilderTemplate', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket'), createEmptyInstance: create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - Bucket_DynamicBuilderTemplate clone() => Bucket_DynamicBuilderTemplate()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - Bucket_DynamicBuilderTemplate copyWith(void Function(Bucket_DynamicBuilderTemplate) updates) => - super.copyWith((message) => updates(message as Bucket_DynamicBuilderTemplate)) as Bucket_DynamicBuilderTemplate; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static Bucket_DynamicBuilderTemplate create() => Bucket_DynamicBuilderTemplate._(); - Bucket_DynamicBuilderTemplate createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Bucket_DynamicBuilderTemplate getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Bucket_DynamicBuilderTemplate? _defaultInstance; -} - -class Bucket extends $pb.GeneratedMessage { - factory Bucket() => create(); - Bucket._() : super(); - factory Bucket.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory Bucket.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Bucket', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'name') - ..pc(2, _omitFieldNames ? '' : 'acls', $pb.PbFieldType.PM, subBuilder: Acl.create) - ..aOM(3, _omitFieldNames ? '' : 'swarming', subBuilder: Swarming.create) - ..aOS(5, _omitFieldNames ? '' : 'shadow') - ..aOM(6, _omitFieldNames ? '' : 'constraints', subBuilder: Bucket_Constraints.create) - ..aOM(7, _omitFieldNames ? '' : 'dynamicBuilderTemplate', - subBuilder: Bucket_DynamicBuilderTemplate.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - Bucket clone() => Bucket()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - Bucket copyWith(void Function(Bucket) updates) => super.copyWith((message) => updates(message as Bucket)) as Bucket; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static Bucket create() => Bucket._(); - Bucket createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Bucket getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Bucket? _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); - - @$core.Deprecated('This field is deprecated.') - @$pb.TagNumber(2) - $core.List get acls => $_getList(1); - - @$pb.TagNumber(3) - Swarming get swarming => $_getN(2); - @$pb.TagNumber(3) - set swarming(Swarming v) { - setField(3, v); - } - - @$pb.TagNumber(3) - $core.bool hasSwarming() => $_has(2); - @$pb.TagNumber(3) - void clearSwarming() => clearField(3); - @$pb.TagNumber(3) - Swarming ensureSwarming() => $_ensure(2); - - @$pb.TagNumber(5) - $core.String get shadow => $_getSZ(3); - @$pb.TagNumber(5) - set shadow($core.String v) { - $_setString(3, v); - } - - @$pb.TagNumber(5) - $core.bool hasShadow() => $_has(3); - @$pb.TagNumber(5) - void clearShadow() => clearField(5); - - @$pb.TagNumber(6) - Bucket_Constraints get constraints => $_getN(4); - @$pb.TagNumber(6) - set constraints(Bucket_Constraints v) { - setField(6, v); - } - - @$pb.TagNumber(6) - $core.bool hasConstraints() => $_has(4); - @$pb.TagNumber(6) - void clearConstraints() => clearField(6); - @$pb.TagNumber(6) - Bucket_Constraints ensureConstraints() => $_ensure(4); - - @$pb.TagNumber(7) - Bucket_DynamicBuilderTemplate get dynamicBuilderTemplate => $_getN(5); - @$pb.TagNumber(7) - set dynamicBuilderTemplate(Bucket_DynamicBuilderTemplate v) { - setField(7, v); - } - - @$pb.TagNumber(7) - $core.bool hasDynamicBuilderTemplate() => $_has(5); - @$pb.TagNumber(7) - void clearDynamicBuilderTemplate() => clearField(7); - @$pb.TagNumber(7) - Bucket_DynamicBuilderTemplate ensureDynamicBuilderTemplate() => $_ensure(5); -} - -class BuildbucketCfg_Topic extends $pb.GeneratedMessage { - factory BuildbucketCfg_Topic() => create(); - BuildbucketCfg_Topic._() : super(); - factory BuildbucketCfg_Topic.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuildbucketCfg_Topic.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuildbucketCfg.Topic', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'name') - ..e<$1.Compression>(2, _omitFieldNames ? '' : 'compression', $pb.PbFieldType.OE, - defaultOrMaker: $1.Compression.ZLIB, valueOf: $1.Compression.valueOf, enumValues: $1.Compression.values) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuildbucketCfg_Topic clone() => BuildbucketCfg_Topic()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuildbucketCfg_Topic copyWith(void Function(BuildbucketCfg_Topic) updates) => - super.copyWith((message) => updates(message as BuildbucketCfg_Topic)) as BuildbucketCfg_Topic; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuildbucketCfg_Topic create() => BuildbucketCfg_Topic._(); - BuildbucketCfg_Topic createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuildbucketCfg_Topic getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuildbucketCfg_Topic? _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) - $1.Compression get compression => $_getN(1); - @$pb.TagNumber(2) - set compression($1.Compression v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasCompression() => $_has(1); - @$pb.TagNumber(2) - void clearCompression() => clearField(2); -} - -class BuildbucketCfg_CommonConfig extends $pb.GeneratedMessage { - factory BuildbucketCfg_CommonConfig() => create(); - BuildbucketCfg_CommonConfig._() : super(); - factory BuildbucketCfg_CommonConfig.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuildbucketCfg_CommonConfig.fromJson($core.String i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuildbucketCfg.CommonConfig', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket'), createEmptyInstance: create) - ..pc(1, _omitFieldNames ? '' : 'buildsNotificationTopics', $pb.PbFieldType.PM, - subBuilder: BuildbucketCfg_Topic.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuildbucketCfg_CommonConfig clone() => BuildbucketCfg_CommonConfig()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuildbucketCfg_CommonConfig copyWith(void Function(BuildbucketCfg_CommonConfig) updates) => - super.copyWith((message) => updates(message as BuildbucketCfg_CommonConfig)) as BuildbucketCfg_CommonConfig; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuildbucketCfg_CommonConfig create() => BuildbucketCfg_CommonConfig._(); - BuildbucketCfg_CommonConfig createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuildbucketCfg_CommonConfig getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuildbucketCfg_CommonConfig? _defaultInstance; - - @$pb.TagNumber(1) - $core.List get buildsNotificationTopics => $_getList(0); -} - -class BuildbucketCfg extends $pb.GeneratedMessage { - factory BuildbucketCfg() => create(); - BuildbucketCfg._() : super(); - factory BuildbucketCfg.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuildbucketCfg.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuildbucketCfg', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket'), createEmptyInstance: create) - ..pc(1, _omitFieldNames ? '' : 'buckets', $pb.PbFieldType.PM, subBuilder: Bucket.create) - ..aOM(5, _omitFieldNames ? '' : 'commonConfig', - subBuilder: BuildbucketCfg_CommonConfig.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuildbucketCfg clone() => BuildbucketCfg()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuildbucketCfg copyWith(void Function(BuildbucketCfg) updates) => - super.copyWith((message) => updates(message as BuildbucketCfg)) as BuildbucketCfg; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuildbucketCfg create() => BuildbucketCfg._(); - BuildbucketCfg createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuildbucketCfg getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuildbucketCfg? _defaultInstance; - - @$pb.TagNumber(1) - $core.List get buckets => $_getList(0); - - @$pb.TagNumber(5) - BuildbucketCfg_CommonConfig get commonConfig => $_getN(1); - @$pb.TagNumber(5) - set commonConfig(BuildbucketCfg_CommonConfig v) { - setField(5, v); - } - - @$pb.TagNumber(5) - $core.bool hasCommonConfig() => $_has(1); - @$pb.TagNumber(5) - void clearCommonConfig() => clearField(5); - @$pb.TagNumber(5) - BuildbucketCfg_CommonConfig ensureCommonConfig() => $_ensure(1); -} - -const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); -const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/project_config.pbenum.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/project_config.pbenum.dart deleted file mode 100644 index 9b8f4e468..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/project_config.pbenum.dart +++ /dev/null @@ -1,50 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/project_config.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:core' as $core; - -import 'package:protobuf/protobuf.dart' as $pb; - -class Toggle extends $pb.ProtobufEnum { - static const Toggle UNSET = Toggle._(0, _omitEnumNames ? '' : 'UNSET'); - static const Toggle YES = Toggle._(1, _omitEnumNames ? '' : 'YES'); - static const Toggle NO = Toggle._(2, _omitEnumNames ? '' : 'NO'); - - static const $core.List values = [ - UNSET, - YES, - NO, - ]; - - static final $core.Map<$core.int, Toggle> _byValue = $pb.ProtobufEnum.initByValue(values); - static Toggle? valueOf($core.int value) => _byValue[value]; - - const Toggle._($core.int v, $core.String n) : super(v, n); -} - -class Acl_Role extends $pb.ProtobufEnum { - static const Acl_Role READER = Acl_Role._(0, _omitEnumNames ? '' : 'READER'); - static const Acl_Role SCHEDULER = Acl_Role._(1, _omitEnumNames ? '' : 'SCHEDULER'); - static const Acl_Role WRITER = Acl_Role._(2, _omitEnumNames ? '' : 'WRITER'); - - static const $core.List values = [ - READER, - SCHEDULER, - WRITER, - ]; - - static final $core.Map<$core.int, Acl_Role> _byValue = $pb.ProtobufEnum.initByValue(values); - static Acl_Role? valueOf($core.int value) => _byValue[value]; - - const Acl_Role._($core.int v, $core.String n) : super(v, n); -} - -const _omitEnumNames = $core.bool.fromEnvironment('protobuf.omit_enum_names'); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/project_config.pbjson.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/project_config.pbjson.dart deleted file mode 100644 index 6a5d66f43..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/project_config.pbjson.dart +++ /dev/null @@ -1,466 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/project_config.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:convert' as $convert; -import 'dart:core' as $core; -import 'dart:typed_data' as $typed_data; - -@$core.Deprecated('Use toggleDescriptor instead') -const Toggle$json = { - '1': 'Toggle', - '2': [ - {'1': 'UNSET', '2': 0}, - {'1': 'YES', '2': 1}, - {'1': 'NO', '2': 2}, - ], -}; - -/// Descriptor for `Toggle`. Decode as a `google.protobuf.EnumDescriptorProto`. -final $typed_data.Uint8List toggleDescriptor = - $convert.base64Decode('CgZUb2dnbGUSCQoFVU5TRVQQABIHCgNZRVMQARIGCgJOTxAC'); - -@$core.Deprecated('Use aclDescriptor instead') -const Acl$json = { - '1': 'Acl', - '2': [ - { - '1': 'role', - '3': 1, - '4': 1, - '5': 14, - '6': '.buildbucket.Acl.Role', - '8': {'3': true}, - '10': 'role', - }, - { - '1': 'group', - '3': 2, - '4': 1, - '5': 9, - '8': {'3': true}, - '10': 'group', - }, - { - '1': 'identity', - '3': 3, - '4': 1, - '5': 9, - '8': {'3': true}, - '10': 'identity', - }, - ], - '4': [Acl_Role$json], -}; - -@$core.Deprecated('Use aclDescriptor instead') -const Acl_Role$json = { - '1': 'Role', - '2': [ - {'1': 'READER', '2': 0}, - {'1': 'SCHEDULER', '2': 1}, - {'1': 'WRITER', '2': 2}, - ], -}; - -/// Descriptor for `Acl`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List aclDescriptor = - $convert.base64Decode('CgNBY2wSLQoEcm9sZRgBIAEoDjIVLmJ1aWxkYnVja2V0LkFjbC5Sb2xlQgIYAVIEcm9sZRIYCg' - 'Vncm91cBgCIAEoCUICGAFSBWdyb3VwEh4KCGlkZW50aXR5GAMgASgJQgIYAVIIaWRlbnRpdHki' - 'LQoEUm9sZRIKCgZSRUFERVIQABINCglTQ0hFRFVMRVIQARIKCgZXUklURVIQAg=='); - -@$core.Deprecated('Use builderConfigDescriptor instead') -const BuilderConfig$json = { - '1': 'BuilderConfig', - '2': [ - {'1': 'name', '3': 1, '4': 1, '5': 9, '10': 'name'}, - {'1': 'backend', '3': 32, '4': 1, '5': 11, '6': '.buildbucket.BuilderConfig.Backend', '10': 'backend'}, - {'1': 'backend_alt', '3': 33, '4': 1, '5': 11, '6': '.buildbucket.BuilderConfig.Backend', '10': 'backendAlt'}, - {'1': 'swarming_host', '3': 21, '4': 1, '5': 9, '10': 'swarmingHost'}, - {'1': 'category', '3': 6, '4': 1, '5': 9, '10': 'category'}, - {'1': 'swarming_tags', '3': 2, '4': 3, '5': 9, '10': 'swarmingTags'}, - {'1': 'dimensions', '3': 3, '4': 3, '5': 9, '10': 'dimensions'}, - {'1': 'recipe', '3': 4, '4': 1, '5': 11, '6': '.buildbucket.BuilderConfig.Recipe', '10': 'recipe'}, - {'1': 'exe', '3': 23, '4': 1, '5': 11, '6': '.buildbucket.v2.Executable', '10': 'exe'}, - {'1': 'properties', '3': 24, '4': 1, '5': 9, '8': {}, '10': 'properties'}, - {'1': 'allowed_property_overrides', '3': 34, '4': 3, '5': 9, '10': 'allowedPropertyOverrides'}, - {'1': 'priority', '3': 5, '4': 1, '5': 13, '10': 'priority'}, - {'1': 'execution_timeout_secs', '3': 7, '4': 1, '5': 13, '10': 'executionTimeoutSecs'}, - {'1': 'expiration_secs', '3': 20, '4': 1, '5': 13, '10': 'expirationSecs'}, - {'1': 'grace_period', '3': 31, '4': 1, '5': 11, '6': '.google.protobuf.Duration', '10': 'gracePeriod'}, - {'1': 'wait_for_capacity', '3': 29, '4': 1, '5': 14, '6': '.buildbucket.v2.Trinary', '10': 'waitForCapacity'}, - {'1': 'caches', '3': 9, '4': 3, '5': 11, '6': '.buildbucket.BuilderConfig.CacheEntry', '10': 'caches'}, - {'1': 'build_numbers', '3': 16, '4': 1, '5': 14, '6': '.buildbucket.Toggle', '10': 'buildNumbers'}, - {'1': 'service_account', '3': 12, '4': 1, '5': 9, '10': 'serviceAccount'}, - {'1': 'auto_builder_dimension', '3': 17, '4': 1, '5': 14, '6': '.buildbucket.Toggle', '10': 'autoBuilderDimension'}, - {'1': 'experimental', '3': 18, '4': 1, '5': 14, '6': '.buildbucket.Toggle', '10': 'experimental'}, - { - '1': 'task_template_canary_percentage', - '3': 22, - '4': 1, - '5': 11, - '6': '.google.protobuf.UInt32Value', - '10': 'taskTemplateCanaryPercentage' - }, - { - '1': 'experiments', - '3': 28, - '4': 3, - '5': 11, - '6': '.buildbucket.BuilderConfig.ExperimentsEntry', - '10': 'experiments' - }, - {'1': 'critical', '3': 25, '4': 1, '5': 14, '6': '.buildbucket.v2.Trinary', '10': 'critical'}, - {'1': 'resultdb', '3': 26, '4': 1, '5': 11, '6': '.buildbucket.BuilderConfig.ResultDB', '10': 'resultdb'}, - {'1': 'description_html', '3': 30, '4': 1, '5': 9, '10': 'descriptionHtml'}, - { - '1': 'shadow_builder_adjustments', - '3': 35, - '4': 1, - '5': 11, - '6': '.buildbucket.BuilderConfig.ShadowBuilderAdjustments', - '10': 'shadowBuilderAdjustments' - }, - {'1': 'retriable', '3': 36, '4': 1, '5': 14, '6': '.buildbucket.v2.Trinary', '10': 'retriable'}, - { - '1': 'builder_health_metrics_links', - '3': 37, - '4': 1, - '5': 11, - '6': '.buildbucket.BuilderConfig.BuilderHealthLinks', - '10': 'builderHealthMetricsLinks' - }, - {'1': 'contact_team_email', '3': 38, '4': 1, '5': 9, '10': 'contactTeamEmail'}, - ], - '3': [ - BuilderConfig_CacheEntry$json, - BuilderConfig_Recipe$json, - BuilderConfig_ResultDB$json, - BuilderConfig_Backend$json, - BuilderConfig_ExperimentsEntry$json, - BuilderConfig_ShadowBuilderAdjustments$json, - BuilderConfig_BuilderHealthLinks$json - ], - '9': [ - {'1': 8, '2': 9}, - {'1': 11, '2': 12}, - {'1': 13, '2': 14}, - {'1': 15, '2': 16}, - {'1': 19, '2': 20}, - {'1': 27, '2': 28}, - {'1': 10, '2': 11}, - ], -}; - -@$core.Deprecated('Use builderConfigDescriptor instead') -const BuilderConfig_CacheEntry$json = { - '1': 'CacheEntry', - '2': [ - {'1': 'name', '3': 1, '4': 1, '5': 9, '10': 'name'}, - {'1': 'path', '3': 2, '4': 1, '5': 9, '10': 'path'}, - {'1': 'wait_for_warm_cache_secs', '3': 3, '4': 1, '5': 5, '10': 'waitForWarmCacheSecs'}, - {'1': 'env_var', '3': 4, '4': 1, '5': 9, '10': 'envVar'}, - ], -}; - -@$core.Deprecated('Use builderConfigDescriptor instead') -const BuilderConfig_Recipe$json = { - '1': 'Recipe', - '2': [ - {'1': 'name', '3': 2, '4': 1, '5': 9, '10': 'name'}, - {'1': 'cipd_package', '3': 6, '4': 1, '5': 9, '10': 'cipdPackage'}, - {'1': 'cipd_version', '3': 5, '4': 1, '5': 9, '10': 'cipdVersion'}, - {'1': 'properties', '3': 3, '4': 3, '5': 9, '10': 'properties'}, - {'1': 'properties_j', '3': 4, '4': 3, '5': 9, '10': 'propertiesJ'}, - ], - '9': [ - {'1': 1, '2': 2}, - ], -}; - -@$core.Deprecated('Use builderConfigDescriptor instead') -const BuilderConfig_ResultDB$json = { - '1': 'ResultDB', - '2': [ - {'1': 'enable', '3': 1, '4': 1, '5': 8, '10': 'enable'}, - {'1': 'bq_exports', '3': 2, '4': 3, '5': 11, '6': '.luci.resultdb.v1.BigQueryExport', '10': 'bqExports'}, - {'1': 'history_options', '3': 3, '4': 1, '5': 11, '6': '.luci.resultdb.v1.HistoryOptions', '10': 'historyOptions'}, - ], -}; - -@$core.Deprecated('Use builderConfigDescriptor instead') -const BuilderConfig_Backend$json = { - '1': 'Backend', - '2': [ - {'1': 'target', '3': 1, '4': 1, '5': 9, '10': 'target'}, - {'1': 'config_json', '3': 2, '4': 1, '5': 9, '8': {}, '10': 'configJson'}, - ], -}; - -@$core.Deprecated('Use builderConfigDescriptor instead') -const BuilderConfig_ExperimentsEntry$json = { - '1': 'ExperimentsEntry', - '2': [ - {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, - {'1': 'value', '3': 2, '4': 1, '5': 5, '10': 'value'}, - ], - '7': {'7': true}, -}; - -@$core.Deprecated('Use builderConfigDescriptor instead') -const BuilderConfig_ShadowBuilderAdjustments$json = { - '1': 'ShadowBuilderAdjustments', - '2': [ - {'1': 'service_account', '3': 1, '4': 1, '5': 9, '10': 'serviceAccount'}, - {'1': 'pool', '3': 2, '4': 1, '5': 9, '10': 'pool'}, - {'1': 'properties', '3': 3, '4': 1, '5': 9, '8': {}, '10': 'properties'}, - {'1': 'dimensions', '3': 4, '4': 3, '5': 9, '10': 'dimensions'}, - ], -}; - -@$core.Deprecated('Use builderConfigDescriptor instead') -const BuilderConfig_BuilderHealthLinks$json = { - '1': 'BuilderHealthLinks', - '2': [ - { - '1': 'doc_links', - '3': 1, - '4': 3, - '5': 11, - '6': '.buildbucket.BuilderConfig.BuilderHealthLinks.DocLinksEntry', - '10': 'docLinks' - }, - { - '1': 'data_links', - '3': 2, - '4': 3, - '5': 11, - '6': '.buildbucket.BuilderConfig.BuilderHealthLinks.DataLinksEntry', - '10': 'dataLinks' - }, - ], - '3': [BuilderConfig_BuilderHealthLinks_DocLinksEntry$json, BuilderConfig_BuilderHealthLinks_DataLinksEntry$json], -}; - -@$core.Deprecated('Use builderConfigDescriptor instead') -const BuilderConfig_BuilderHealthLinks_DocLinksEntry$json = { - '1': 'DocLinksEntry', - '2': [ - {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, - {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'}, - ], - '7': {'7': true}, -}; - -@$core.Deprecated('Use builderConfigDescriptor instead') -const BuilderConfig_BuilderHealthLinks_DataLinksEntry$json = { - '1': 'DataLinksEntry', - '2': [ - {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, - {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'}, - ], - '7': {'7': true}, -}; - -/// Descriptor for `BuilderConfig`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List builderConfigDescriptor = - $convert.base64Decode('Cg1CdWlsZGVyQ29uZmlnEhIKBG5hbWUYASABKAlSBG5hbWUSPAoHYmFja2VuZBggIAEoCzIiLm' - 'J1aWxkYnVja2V0LkJ1aWxkZXJDb25maWcuQmFja2VuZFIHYmFja2VuZBJDCgtiYWNrZW5kX2Fs' - 'dBghIAEoCzIiLmJ1aWxkYnVja2V0LkJ1aWxkZXJDb25maWcuQmFja2VuZFIKYmFja2VuZEFsdB' - 'IjCg1zd2FybWluZ19ob3N0GBUgASgJUgxzd2FybWluZ0hvc3QSGgoIY2F0ZWdvcnkYBiABKAlS' - 'CGNhdGVnb3J5EiMKDXN3YXJtaW5nX3RhZ3MYAiADKAlSDHN3YXJtaW5nVGFncxIeCgpkaW1lbn' - 'Npb25zGAMgAygJUgpkaW1lbnNpb25zEjkKBnJlY2lwZRgEIAEoCzIhLmJ1aWxkYnVja2V0LkJ1' - 'aWxkZXJDb25maWcuUmVjaXBlUgZyZWNpcGUSLAoDZXhlGBcgASgLMhouYnVpbGRidWNrZXQudj' - 'IuRXhlY3V0YWJsZVIDZXhlEiQKCnByb3BlcnRpZXMYGCABKAlCBKj+IwFSCnByb3BlcnRpZXMS' - 'PAoaYWxsb3dlZF9wcm9wZXJ0eV9vdmVycmlkZXMYIiADKAlSGGFsbG93ZWRQcm9wZXJ0eU92ZX' - 'JyaWRlcxIaCghwcmlvcml0eRgFIAEoDVIIcHJpb3JpdHkSNAoWZXhlY3V0aW9uX3RpbWVvdXRf' - 'c2VjcxgHIAEoDVIUZXhlY3V0aW9uVGltZW91dFNlY3MSJwoPZXhwaXJhdGlvbl9zZWNzGBQgAS' - 'gNUg5leHBpcmF0aW9uU2VjcxI8CgxncmFjZV9wZXJpb2QYHyABKAsyGS5nb29nbGUucHJvdG9i' - 'dWYuRHVyYXRpb25SC2dyYWNlUGVyaW9kEkMKEXdhaXRfZm9yX2NhcGFjaXR5GB0gASgOMhcuYn' - 'VpbGRidWNrZXQudjIuVHJpbmFyeVIPd2FpdEZvckNhcGFjaXR5Ej0KBmNhY2hlcxgJIAMoCzIl' - 'LmJ1aWxkYnVja2V0LkJ1aWxkZXJDb25maWcuQ2FjaGVFbnRyeVIGY2FjaGVzEjgKDWJ1aWxkX2' - '51bWJlcnMYECABKA4yEy5idWlsZGJ1Y2tldC5Ub2dnbGVSDGJ1aWxkTnVtYmVycxInCg9zZXJ2' - 'aWNlX2FjY291bnQYDCABKAlSDnNlcnZpY2VBY2NvdW50EkkKFmF1dG9fYnVpbGRlcl9kaW1lbn' - 'Npb24YESABKA4yEy5idWlsZGJ1Y2tldC5Ub2dnbGVSFGF1dG9CdWlsZGVyRGltZW5zaW9uEjcK' - 'DGV4cGVyaW1lbnRhbBgSIAEoDjITLmJ1aWxkYnVja2V0LlRvZ2dsZVIMZXhwZXJpbWVudGFsEm' - 'MKH3Rhc2tfdGVtcGxhdGVfY2FuYXJ5X3BlcmNlbnRhZ2UYFiABKAsyHC5nb29nbGUucHJvdG9i' - 'dWYuVUludDMyVmFsdWVSHHRhc2tUZW1wbGF0ZUNhbmFyeVBlcmNlbnRhZ2USTQoLZXhwZXJpbW' - 'VudHMYHCADKAsyKy5idWlsZGJ1Y2tldC5CdWlsZGVyQ29uZmlnLkV4cGVyaW1lbnRzRW50cnlS' - 'C2V4cGVyaW1lbnRzEjMKCGNyaXRpY2FsGBkgASgOMhcuYnVpbGRidWNrZXQudjIuVHJpbmFyeV' - 'IIY3JpdGljYWwSPwoIcmVzdWx0ZGIYGiABKAsyIy5idWlsZGJ1Y2tldC5CdWlsZGVyQ29uZmln' - 'LlJlc3VsdERCUghyZXN1bHRkYhIpChBkZXNjcmlwdGlvbl9odG1sGB4gASgJUg9kZXNjcmlwdG' - 'lvbkh0bWwScQoac2hhZG93X2J1aWxkZXJfYWRqdXN0bWVudHMYIyABKAsyMy5idWlsZGJ1Y2tl' - 'dC5CdWlsZGVyQ29uZmlnLlNoYWRvd0J1aWxkZXJBZGp1c3RtZW50c1IYc2hhZG93QnVpbGRlck' - 'FkanVzdG1lbnRzEjUKCXJldHJpYWJsZRgkIAEoDjIXLmJ1aWxkYnVja2V0LnYyLlRyaW5hcnlS' - 'CXJldHJpYWJsZRJuChxidWlsZGVyX2hlYWx0aF9tZXRyaWNzX2xpbmtzGCUgASgLMi0uYnVpbG' - 'RidWNrZXQuQnVpbGRlckNvbmZpZy5CdWlsZGVySGVhbHRoTGlua3NSGWJ1aWxkZXJIZWFsdGhN' - 'ZXRyaWNzTGlua3MSLAoSY29udGFjdF90ZWFtX2VtYWlsGCYgASgJUhBjb250YWN0VGVhbUVtYW' - 'lsGoUBCgpDYWNoZUVudHJ5EhIKBG5hbWUYASABKAlSBG5hbWUSEgoEcGF0aBgCIAEoCVIEcGF0' - 'aBI2Chh3YWl0X2Zvcl93YXJtX2NhY2hlX3NlY3MYAyABKAVSFHdhaXRGb3JXYXJtQ2FjaGVTZW' - 'NzEhcKB2Vudl92YXIYBCABKAlSBmVudlZhchqrAQoGUmVjaXBlEhIKBG5hbWUYAiABKAlSBG5h' - 'bWUSIQoMY2lwZF9wYWNrYWdlGAYgASgJUgtjaXBkUGFja2FnZRIhCgxjaXBkX3ZlcnNpb24YBS' - 'ABKAlSC2NpcGRWZXJzaW9uEh4KCnByb3BlcnRpZXMYAyADKAlSCnByb3BlcnRpZXMSIQoMcHJv' - 'cGVydGllc19qGAQgAygJUgtwcm9wZXJ0aWVzSkoECAEQAhquAQoIUmVzdWx0REISFgoGZW5hYm' - 'xlGAEgASgIUgZlbmFibGUSPwoKYnFfZXhwb3J0cxgCIAMoCzIgLmx1Y2kucmVzdWx0ZGIudjEu' - 'QmlnUXVlcnlFeHBvcnRSCWJxRXhwb3J0cxJJCg9oaXN0b3J5X29wdGlvbnMYAyABKAsyIC5sdW' - 'NpLnJlc3VsdGRiLnYxLkhpc3RvcnlPcHRpb25zUg5oaXN0b3J5T3B0aW9ucxpICgdCYWNrZW5k' - 'EhYKBnRhcmdldBgBIAEoCVIGdGFyZ2V0EiUKC2NvbmZpZ19qc29uGAIgASgJQgSo/iMBUgpjb2' - '5maWdKc29uGj4KEEV4cGVyaW1lbnRzRW50cnkSEAoDa2V5GAEgASgJUgNrZXkSFAoFdmFsdWUY' - 'AiABKAVSBXZhbHVlOgI4ARqdAQoYU2hhZG93QnVpbGRlckFkanVzdG1lbnRzEicKD3NlcnZpY2' - 'VfYWNjb3VudBgBIAEoCVIOc2VydmljZUFjY291bnQSEgoEcG9vbBgCIAEoCVIEcG9vbBIkCgpw' - 'cm9wZXJ0aWVzGAMgASgJQgSo/iMBUgpwcm9wZXJ0aWVzEh4KCmRpbWVuc2lvbnMYBCADKAlSCm' - 'RpbWVuc2lvbnMaxgIKEkJ1aWxkZXJIZWFsdGhMaW5rcxJYCglkb2NfbGlua3MYASADKAsyOy5i' - 'dWlsZGJ1Y2tldC5CdWlsZGVyQ29uZmlnLkJ1aWxkZXJIZWFsdGhMaW5rcy5Eb2NMaW5rc0VudH' - 'J5Ughkb2NMaW5rcxJbCgpkYXRhX2xpbmtzGAIgAygLMjwuYnVpbGRidWNrZXQuQnVpbGRlckNv' - 'bmZpZy5CdWlsZGVySGVhbHRoTGlua3MuRGF0YUxpbmtzRW50cnlSCWRhdGFMaW5rcxo7Cg1Eb2' - 'NMaW5rc0VudHJ5EhAKA2tleRgBIAEoCVIDa2V5EhQKBXZhbHVlGAIgASgJUgV2YWx1ZToCOAEa' - 'PAoORGF0YUxpbmtzRW50cnkSEAoDa2V5GAEgASgJUgNrZXkSFAoFdmFsdWUYAiABKAlSBXZhbH' - 'VlOgI4AUoECAgQCUoECAsQDEoECA0QDkoECA8QEEoECBMQFEoECBsQHEoECAoQCw=='); - -@$core.Deprecated('Use swarmingDescriptor instead') -const Swarming$json = { - '1': 'Swarming', - '2': [ - {'1': 'builders', '3': 4, '4': 3, '5': 11, '6': '.buildbucket.BuilderConfig', '10': 'builders'}, - { - '1': 'task_template_canary_percentage', - '3': 5, - '4': 1, - '5': 11, - '6': '.google.protobuf.UInt32Value', - '10': 'taskTemplateCanaryPercentage' - }, - ], - '9': [ - {'1': 1, '2': 2}, - {'1': 2, '2': 3}, - {'1': 3, '2': 4}, - ], -}; - -/// Descriptor for `Swarming`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List swarmingDescriptor = - $convert.base64Decode('CghTd2FybWluZxI2CghidWlsZGVycxgEIAMoCzIaLmJ1aWxkYnVja2V0LkJ1aWxkZXJDb25maW' - 'dSCGJ1aWxkZXJzEmMKH3Rhc2tfdGVtcGxhdGVfY2FuYXJ5X3BlcmNlbnRhZ2UYBSABKAsyHC5n' - 'b29nbGUucHJvdG9idWYuVUludDMyVmFsdWVSHHRhc2tUZW1wbGF0ZUNhbmFyeVBlcmNlbnRhZ2' - 'VKBAgBEAJKBAgCEANKBAgDEAQ='); - -@$core.Deprecated('Use bucketDescriptor instead') -const Bucket$json = { - '1': 'Bucket', - '2': [ - {'1': 'name', '3': 1, '4': 1, '5': 9, '10': 'name'}, - { - '1': 'acls', - '3': 2, - '4': 3, - '5': 11, - '6': '.buildbucket.Acl', - '8': {'3': true}, - '10': 'acls', - }, - {'1': 'swarming', '3': 3, '4': 1, '5': 11, '6': '.buildbucket.Swarming', '10': 'swarming'}, - {'1': 'shadow', '3': 5, '4': 1, '5': 9, '10': 'shadow'}, - {'1': 'constraints', '3': 6, '4': 1, '5': 11, '6': '.buildbucket.Bucket.Constraints', '10': 'constraints'}, - { - '1': 'dynamic_builder_template', - '3': 7, - '4': 1, - '5': 11, - '6': '.buildbucket.Bucket.DynamicBuilderTemplate', - '10': 'dynamicBuilderTemplate' - }, - ], - '3': [Bucket_Constraints$json, Bucket_DynamicBuilderTemplate$json], - '9': [ - {'1': 4, '2': 5}, - ], -}; - -@$core.Deprecated('Use bucketDescriptor instead') -const Bucket_Constraints$json = { - '1': 'Constraints', - '2': [ - {'1': 'pools', '3': 1, '4': 3, '5': 9, '10': 'pools'}, - {'1': 'service_accounts', '3': 2, '4': 3, '5': 9, '10': 'serviceAccounts'}, - ], -}; - -@$core.Deprecated('Use bucketDescriptor instead') -const Bucket_DynamicBuilderTemplate$json = { - '1': 'DynamicBuilderTemplate', -}; - -/// Descriptor for `Bucket`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List bucketDescriptor = - $convert.base64Decode('CgZCdWNrZXQSEgoEbmFtZRgBIAEoCVIEbmFtZRIoCgRhY2xzGAIgAygLMhAuYnVpbGRidWNrZX' - 'QuQWNsQgIYAVIEYWNscxIxCghzd2FybWluZxgDIAEoCzIVLmJ1aWxkYnVja2V0LlN3YXJtaW5n' - 'Ughzd2FybWluZxIWCgZzaGFkb3cYBSABKAlSBnNoYWRvdxJBCgtjb25zdHJhaW50cxgGIAEoCz' - 'IfLmJ1aWxkYnVja2V0LkJ1Y2tldC5Db25zdHJhaW50c1ILY29uc3RyYWludHMSZAoYZHluYW1p' - 'Y19idWlsZGVyX3RlbXBsYXRlGAcgASgLMiouYnVpbGRidWNrZXQuQnVja2V0LkR5bmFtaWNCdW' - 'lsZGVyVGVtcGxhdGVSFmR5bmFtaWNCdWlsZGVyVGVtcGxhdGUaTgoLQ29uc3RyYWludHMSFAoF' - 'cG9vbHMYASADKAlSBXBvb2xzEikKEHNlcnZpY2VfYWNjb3VudHMYAiADKAlSD3NlcnZpY2VBY2' - 'NvdW50cxoYChZEeW5hbWljQnVpbGRlclRlbXBsYXRlSgQIBBAF'); - -@$core.Deprecated('Use buildbucketCfgDescriptor instead') -const BuildbucketCfg$json = { - '1': 'BuildbucketCfg', - '2': [ - {'1': 'buckets', '3': 1, '4': 3, '5': 11, '6': '.buildbucket.Bucket', '10': 'buckets'}, - { - '1': 'common_config', - '3': 5, - '4': 1, - '5': 11, - '6': '.buildbucket.BuildbucketCfg.CommonConfig', - '10': 'commonConfig' - }, - ], - '3': [BuildbucketCfg_Topic$json, BuildbucketCfg_CommonConfig$json], - '9': [ - {'1': 2, '2': 3}, - {'1': 3, '2': 4}, - {'1': 4, '2': 5}, - ], -}; - -@$core.Deprecated('Use buildbucketCfgDescriptor instead') -const BuildbucketCfg_Topic$json = { - '1': 'Topic', - '2': [ - {'1': 'name', '3': 1, '4': 1, '5': 9, '10': 'name'}, - {'1': 'compression', '3': 2, '4': 1, '5': 14, '6': '.buildbucket.v2.Compression', '10': 'compression'}, - ], -}; - -@$core.Deprecated('Use buildbucketCfgDescriptor instead') -const BuildbucketCfg_CommonConfig$json = { - '1': 'CommonConfig', - '2': [ - { - '1': 'builds_notification_topics', - '3': 1, - '4': 3, - '5': 11, - '6': '.buildbucket.BuildbucketCfg.Topic', - '10': 'buildsNotificationTopics' - }, - ], -}; - -/// Descriptor for `BuildbucketCfg`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List buildbucketCfgDescriptor = - $convert.base64Decode('Cg5CdWlsZGJ1Y2tldENmZxItCgdidWNrZXRzGAEgAygLMhMuYnVpbGRidWNrZXQuQnVja2V0Ug' - 'didWNrZXRzEk0KDWNvbW1vbl9jb25maWcYBSABKAsyKC5idWlsZGJ1Y2tldC5CdWlsZGJ1Y2tl' - 'dENmZy5Db21tb25Db25maWdSDGNvbW1vbkNvbmZpZxpaCgVUb3BpYxISCgRuYW1lGAEgASgJUg' - 'RuYW1lEj0KC2NvbXByZXNzaW9uGAIgASgOMhsuYnVpbGRidWNrZXQudjIuQ29tcHJlc3Npb25S' - 'C2NvbXByZXNzaW9uGm8KDENvbW1vbkNvbmZpZxJfChpidWlsZHNfbm90aWZpY2F0aW9uX3RvcG' - 'ljcxgBIAMoCzIhLmJ1aWxkYnVja2V0LkJ1aWxkYnVja2V0Q2ZnLlRvcGljUhhidWlsZHNOb3Rp' - 'ZmljYXRpb25Ub3BpY3NKBAgCEANKBAgDEARKBAgEEAU='); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/project_config.pbserver.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/project_config.pbserver.dart deleted file mode 100644 index b1b47d385..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/project_config.pbserver.dart +++ /dev/null @@ -1,8 +0,0 @@ -/// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/project_config.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 'project_config.pb.dart'; diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/step.pb.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/step.pb.dart deleted file mode 100644 index d605176a6..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/step.pb.dart +++ /dev/null @@ -1,207 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/step.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:core' as $core; - -import 'package:protobuf/protobuf.dart' as $pb; - -import '../../../../google/protobuf/timestamp.pb.dart' as $0; -import 'common.pb.dart' as $1; -import 'common.pbenum.dart' as $1; - -class Step_MergeBuild extends $pb.GeneratedMessage { - factory Step_MergeBuild() => create(); - Step_MergeBuild._() : super(); - factory Step_MergeBuild.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory Step_MergeBuild.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Step.MergeBuild', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'fromLogdogStream') - ..aOB(2, _omitFieldNames ? '' : 'legacyGlobalNamespace') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - Step_MergeBuild clone() => Step_MergeBuild()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - Step_MergeBuild copyWith(void Function(Step_MergeBuild) updates) => - super.copyWith((message) => updates(message as Step_MergeBuild)) as Step_MergeBuild; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static Step_MergeBuild create() => Step_MergeBuild._(); - Step_MergeBuild createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Step_MergeBuild getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Step_MergeBuild? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get fromLogdogStream => $_getSZ(0); - @$pb.TagNumber(1) - set fromLogdogStream($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasFromLogdogStream() => $_has(0); - @$pb.TagNumber(1) - void clearFromLogdogStream() => clearField(1); - - @$pb.TagNumber(2) - $core.bool get legacyGlobalNamespace => $_getBF(1); - @$pb.TagNumber(2) - set legacyGlobalNamespace($core.bool v) { - $_setBool(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasLegacyGlobalNamespace() => $_has(1); - @$pb.TagNumber(2) - void clearLegacyGlobalNamespace() => clearField(2); -} - -class Step extends $pb.GeneratedMessage { - factory Step() => create(); - Step._() : super(); - factory Step.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory Step.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Step', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'name') - ..aOM<$0.Timestamp>(2, _omitFieldNames ? '' : 'startTime', subBuilder: $0.Timestamp.create) - ..aOM<$0.Timestamp>(3, _omitFieldNames ? '' : 'endTime', subBuilder: $0.Timestamp.create) - ..e<$1.Status>(4, _omitFieldNames ? '' : 'status', $pb.PbFieldType.OE, - defaultOrMaker: $1.Status.STATUS_UNSPECIFIED, valueOf: $1.Status.valueOf, enumValues: $1.Status.values) - ..pc<$1.Log>(5, _omitFieldNames ? '' : 'logs', $pb.PbFieldType.PM, subBuilder: $1.Log.create) - ..aOM(6, _omitFieldNames ? '' : 'mergeBuild', subBuilder: Step_MergeBuild.create) - ..aOS(7, _omitFieldNames ? '' : 'summaryMarkdown') - ..pc<$1.StringPair>(8, _omitFieldNames ? '' : 'tags', $pb.PbFieldType.PM, subBuilder: $1.StringPair.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - Step clone() => Step()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - Step copyWith(void Function(Step) updates) => super.copyWith((message) => updates(message as Step)) as Step; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static Step create() => Step._(); - Step createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Step getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Step? _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) - $0.Timestamp get startTime => $_getN(1); - @$pb.TagNumber(2) - set startTime($0.Timestamp v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasStartTime() => $_has(1); - @$pb.TagNumber(2) - void clearStartTime() => clearField(2); - @$pb.TagNumber(2) - $0.Timestamp ensureStartTime() => $_ensure(1); - - @$pb.TagNumber(3) - $0.Timestamp get endTime => $_getN(2); - @$pb.TagNumber(3) - set endTime($0.Timestamp v) { - setField(3, v); - } - - @$pb.TagNumber(3) - $core.bool hasEndTime() => $_has(2); - @$pb.TagNumber(3) - void clearEndTime() => clearField(3); - @$pb.TagNumber(3) - $0.Timestamp ensureEndTime() => $_ensure(2); - - @$pb.TagNumber(4) - $1.Status get status => $_getN(3); - @$pb.TagNumber(4) - set status($1.Status v) { - setField(4, v); - } - - @$pb.TagNumber(4) - $core.bool hasStatus() => $_has(3); - @$pb.TagNumber(4) - void clearStatus() => clearField(4); - - @$pb.TagNumber(5) - $core.List<$1.Log> get logs => $_getList(4); - - @$pb.TagNumber(6) - Step_MergeBuild get mergeBuild => $_getN(5); - @$pb.TagNumber(6) - set mergeBuild(Step_MergeBuild v) { - setField(6, v); - } - - @$pb.TagNumber(6) - $core.bool hasMergeBuild() => $_has(5); - @$pb.TagNumber(6) - void clearMergeBuild() => clearField(6); - @$pb.TagNumber(6) - Step_MergeBuild ensureMergeBuild() => $_ensure(5); - - @$pb.TagNumber(7) - $core.String get summaryMarkdown => $_getSZ(6); - @$pb.TagNumber(7) - set summaryMarkdown($core.String v) { - $_setString(6, v); - } - - @$pb.TagNumber(7) - $core.bool hasSummaryMarkdown() => $_has(6); - @$pb.TagNumber(7) - void clearSummaryMarkdown() => clearField(7); - - @$pb.TagNumber(8) - $core.List<$1.StringPair> get tags => $_getList(7); -} - -const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); -const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/step.pbenum.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/step.pbenum.dart deleted file mode 100644 index d9c0c8de9..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/step.pbenum.dart +++ /dev/null @@ -1,10 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/step.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/step.pbjson.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/step.pbjson.dart deleted file mode 100644 index 89143da25..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/step.pbjson.dart +++ /dev/null @@ -1,52 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/step.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:convert' as $convert; -import 'dart:core' as $core; -import 'dart:typed_data' as $typed_data; - -@$core.Deprecated('Use stepDescriptor instead') -const Step$json = { - '1': 'Step', - '2': [ - {'1': 'name', '3': 1, '4': 1, '5': 9, '10': 'name'}, - {'1': 'start_time', '3': 2, '4': 1, '5': 11, '6': '.google.protobuf.Timestamp', '10': 'startTime'}, - {'1': 'end_time', '3': 3, '4': 1, '5': 11, '6': '.google.protobuf.Timestamp', '10': 'endTime'}, - {'1': 'status', '3': 4, '4': 1, '5': 14, '6': '.buildbucket.v2.Status', '10': 'status'}, - {'1': 'logs', '3': 5, '4': 3, '5': 11, '6': '.buildbucket.v2.Log', '10': 'logs'}, - {'1': 'merge_build', '3': 6, '4': 1, '5': 11, '6': '.buildbucket.v2.Step.MergeBuild', '10': 'mergeBuild'}, - {'1': 'summary_markdown', '3': 7, '4': 1, '5': 9, '10': 'summaryMarkdown'}, - {'1': 'tags', '3': 8, '4': 3, '5': 11, '6': '.buildbucket.v2.StringPair', '10': 'tags'}, - ], - '3': [Step_MergeBuild$json], -}; - -@$core.Deprecated('Use stepDescriptor instead') -const Step_MergeBuild$json = { - '1': 'MergeBuild', - '2': [ - {'1': 'from_logdog_stream', '3': 1, '4': 1, '5': 9, '10': 'fromLogdogStream'}, - {'1': 'legacy_global_namespace', '3': 2, '4': 1, '5': 8, '10': 'legacyGlobalNamespace'}, - ], -}; - -/// Descriptor for `Step`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List stepDescriptor = - $convert.base64Decode('CgRTdGVwEhIKBG5hbWUYASABKAlSBG5hbWUSOQoKc3RhcnRfdGltZRgCIAEoCzIaLmdvb2dsZS' - '5wcm90b2J1Zi5UaW1lc3RhbXBSCXN0YXJ0VGltZRI1CghlbmRfdGltZRgDIAEoCzIaLmdvb2ds' - 'ZS5wcm90b2J1Zi5UaW1lc3RhbXBSB2VuZFRpbWUSLgoGc3RhdHVzGAQgASgOMhYuYnVpbGRidW' - 'NrZXQudjIuU3RhdHVzUgZzdGF0dXMSJwoEbG9ncxgFIAMoCzITLmJ1aWxkYnVja2V0LnYyLkxv' - 'Z1IEbG9ncxJACgttZXJnZV9idWlsZBgGIAEoCzIfLmJ1aWxkYnVja2V0LnYyLlN0ZXAuTWVyZ2' - 'VCdWlsZFIKbWVyZ2VCdWlsZBIpChBzdW1tYXJ5X21hcmtkb3duGAcgASgJUg9zdW1tYXJ5TWFy' - 'a2Rvd24SLgoEdGFncxgIIAMoCzIaLmJ1aWxkYnVja2V0LnYyLlN0cmluZ1BhaXJSBHRhZ3Macg' - 'oKTWVyZ2VCdWlsZBIsChJmcm9tX2xvZ2RvZ19zdHJlYW0YASABKAlSEGZyb21Mb2dkb2dTdHJl' - 'YW0SNgoXbGVnYWN5X2dsb2JhbF9uYW1lc3BhY2UYAiABKAhSFWxlZ2FjeUdsb2JhbE5hbWVzcG' - 'FjZQ=='); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/step.pbserver.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/step.pbserver.dart deleted file mode 100644 index 9f46e5882..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/step.pbserver.dart +++ /dev/null @@ -1,8 +0,0 @@ -/// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/step.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 'step.pb.dart'; diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/task.pb.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/task.pb.dart deleted file mode 100644 index fdd4ad541..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/task.pb.dart +++ /dev/null @@ -1,273 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/task.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:core' as $core; - -import 'package:fixnum/fixnum.dart' as $fixnum; -import 'package:protobuf/protobuf.dart' as $pb; - -import '../../../../google/protobuf/struct.pb.dart' as $1; -import 'common.pb.dart' as $0; -import 'common.pbenum.dart' as $0; - -class Task extends $pb.GeneratedMessage { - factory Task() => create(); - Task._() : super(); - factory Task.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory Task.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Task', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOM(1, _omitFieldNames ? '' : 'id', subBuilder: TaskID.create) - ..aOS(2, _omitFieldNames ? '' : 'link') - ..e<$0.Status>(3, _omitFieldNames ? '' : 'status', $pb.PbFieldType.OE, - defaultOrMaker: $0.Status.STATUS_UNSPECIFIED, valueOf: $0.Status.valueOf, enumValues: $0.Status.values) - ..aOM<$0.StatusDetails>(4, _omitFieldNames ? '' : 'statusDetails', subBuilder: $0.StatusDetails.create) - ..aOS(5, _omitFieldNames ? '' : 'summaryHtml') - ..aOM<$1.Struct>(6, _omitFieldNames ? '' : 'details', subBuilder: $1.Struct.create) - ..aInt64(7, _omitFieldNames ? '' : 'updateId') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - Task clone() => Task()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - Task copyWith(void Function(Task) updates) => super.copyWith((message) => updates(message as Task)) as Task; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static Task create() => Task._(); - Task createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Task getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Task? _defaultInstance; - - @$pb.TagNumber(1) - TaskID get id => $_getN(0); - @$pb.TagNumber(1) - set id(TaskID v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasId() => $_has(0); - @$pb.TagNumber(1) - void clearId() => clearField(1); - @$pb.TagNumber(1) - TaskID ensureId() => $_ensure(0); - - @$pb.TagNumber(2) - $core.String get link => $_getSZ(1); - @$pb.TagNumber(2) - set link($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasLink() => $_has(1); - @$pb.TagNumber(2) - void clearLink() => clearField(2); - - @$pb.TagNumber(3) - $0.Status get status => $_getN(2); - @$pb.TagNumber(3) - set status($0.Status v) { - setField(3, v); - } - - @$pb.TagNumber(3) - $core.bool hasStatus() => $_has(2); - @$pb.TagNumber(3) - void clearStatus() => clearField(3); - - @$pb.TagNumber(4) - $0.StatusDetails get statusDetails => $_getN(3); - @$pb.TagNumber(4) - set statusDetails($0.StatusDetails v) { - setField(4, v); - } - - @$pb.TagNumber(4) - $core.bool hasStatusDetails() => $_has(3); - @$pb.TagNumber(4) - void clearStatusDetails() => clearField(4); - @$pb.TagNumber(4) - $0.StatusDetails ensureStatusDetails() => $_ensure(3); - - @$pb.TagNumber(5) - $core.String get summaryHtml => $_getSZ(4); - @$pb.TagNumber(5) - set summaryHtml($core.String v) { - $_setString(4, v); - } - - @$pb.TagNumber(5) - $core.bool hasSummaryHtml() => $_has(4); - @$pb.TagNumber(5) - void clearSummaryHtml() => clearField(5); - - @$pb.TagNumber(6) - $1.Struct get details => $_getN(5); - @$pb.TagNumber(6) - set details($1.Struct v) { - setField(6, v); - } - - @$pb.TagNumber(6) - $core.bool hasDetails() => $_has(5); - @$pb.TagNumber(6) - void clearDetails() => clearField(6); - @$pb.TagNumber(6) - $1.Struct ensureDetails() => $_ensure(5); - - @$pb.TagNumber(7) - $fixnum.Int64 get updateId => $_getI64(6); - @$pb.TagNumber(7) - set updateId($fixnum.Int64 v) { - $_setInt64(6, v); - } - - @$pb.TagNumber(7) - $core.bool hasUpdateId() => $_has(6); - @$pb.TagNumber(7) - void clearUpdateId() => clearField(7); -} - -class TaskID extends $pb.GeneratedMessage { - factory TaskID() => create(); - TaskID._() : super(); - factory TaskID.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory TaskID.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'TaskID', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'target') - ..aOS(2, _omitFieldNames ? '' : 'id') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - TaskID clone() => TaskID()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - TaskID copyWith(void Function(TaskID) updates) => super.copyWith((message) => updates(message as TaskID)) as TaskID; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static TaskID create() => TaskID._(); - TaskID createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static TaskID getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static TaskID? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get target => $_getSZ(0); - @$pb.TagNumber(1) - set target($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasTarget() => $_has(0); - @$pb.TagNumber(1) - void clearTarget() => clearField(1); - - @$pb.TagNumber(2) - $core.String get id => $_getSZ(1); - @$pb.TagNumber(2) - set id($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasId() => $_has(1); - @$pb.TagNumber(2) - void clearId() => clearField(2); -} - -class BuildTaskUpdate extends $pb.GeneratedMessage { - factory BuildTaskUpdate() => create(); - BuildTaskUpdate._() : super(); - factory BuildTaskUpdate.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BuildTaskUpdate.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BuildTaskUpdate', - package: const $pb.PackageName(_omitMessageNames ? '' : 'buildbucket.v2'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'buildId') - ..aOM(2, _omitFieldNames ? '' : 'task', subBuilder: Task.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BuildTaskUpdate clone() => BuildTaskUpdate()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BuildTaskUpdate copyWith(void Function(BuildTaskUpdate) updates) => - super.copyWith((message) => updates(message as BuildTaskUpdate)) as BuildTaskUpdate; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BuildTaskUpdate create() => BuildTaskUpdate._(); - BuildTaskUpdate createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BuildTaskUpdate getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BuildTaskUpdate? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get buildId => $_getSZ(0); - @$pb.TagNumber(1) - set buildId($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasBuildId() => $_has(0); - @$pb.TagNumber(1) - void clearBuildId() => clearField(1); - - @$pb.TagNumber(2) - Task get task => $_getN(1); - @$pb.TagNumber(2) - set task(Task v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasTask() => $_has(1); - @$pb.TagNumber(2) - void clearTask() => clearField(2); - @$pb.TagNumber(2) - Task ensureTask() => $_ensure(1); -} - -const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); -const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/task.pbenum.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/task.pbenum.dart deleted file mode 100644 index 82addb037..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/task.pbenum.dart +++ /dev/null @@ -1,10 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/task.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/task.pbjson.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/task.pbjson.dart deleted file mode 100644 index 99099ac21..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/task.pbjson.dart +++ /dev/null @@ -1,65 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/task.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:convert' as $convert; -import 'dart:core' as $core; -import 'dart:typed_data' as $typed_data; - -@$core.Deprecated('Use taskDescriptor instead') -const Task$json = { - '1': 'Task', - '2': [ - {'1': 'id', '3': 1, '4': 1, '5': 11, '6': '.buildbucket.v2.TaskID', '8': {}, '10': 'id'}, - {'1': 'link', '3': 2, '4': 1, '5': 9, '10': 'link'}, - {'1': 'status', '3': 3, '4': 1, '5': 14, '6': '.buildbucket.v2.Status', '8': {}, '10': 'status'}, - {'1': 'status_details', '3': 4, '4': 1, '5': 11, '6': '.buildbucket.v2.StatusDetails', '10': 'statusDetails'}, - {'1': 'summary_html', '3': 5, '4': 1, '5': 9, '10': 'summaryHtml'}, - {'1': 'details', '3': 6, '4': 1, '5': 11, '6': '.google.protobuf.Struct', '10': 'details'}, - {'1': 'update_id', '3': 7, '4': 1, '5': 3, '8': {}, '10': 'updateId'}, - ], -}; - -/// Descriptor for `Task`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List taskDescriptor = - $convert.base64Decode('CgRUYXNrEi4KAmlkGAEgASgLMhYuYnVpbGRidWNrZXQudjIuVGFza0lEQgaSwxoCCAJSAmlkEh' - 'IKBGxpbmsYAiABKAlSBGxpbmsSNgoGc3RhdHVzGAMgASgOMhYuYnVpbGRidWNrZXQudjIuU3Rh' - 'dHVzQgaSwxoCCAJSBnN0YXR1cxJECg5zdGF0dXNfZGV0YWlscxgEIAEoCzIdLmJ1aWxkYnVja2' - 'V0LnYyLlN0YXR1c0RldGFpbHNSDXN0YXR1c0RldGFpbHMSIQoMc3VtbWFyeV9odG1sGAUgASgJ' - 'UgtzdW1tYXJ5SHRtbBIxCgdkZXRhaWxzGAYgASgLMhcuZ29vZ2xlLnByb3RvYnVmLlN0cnVjdF' - 'IHZGV0YWlscxIjCgl1cGRhdGVfaWQYByABKANCBpLDGgIIAlIIdXBkYXRlSWQ='); - -@$core.Deprecated('Use taskIDDescriptor instead') -const TaskID$json = { - '1': 'TaskID', - '2': [ - {'1': 'target', '3': 1, '4': 1, '5': 9, '8': {}, '10': 'target'}, - {'1': 'id', '3': 2, '4': 1, '5': 9, '8': {}, '10': 'id'}, - ], -}; - -/// Descriptor for `TaskID`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List taskIDDescriptor = - $convert.base64Decode('CgZUYXNrSUQSJAoGdGFyZ2V0GAEgASgJQgyKwxoCCAKSwxoCCAJSBnRhcmdldBIWCgJpZBgCIA' - 'EoCUIGksMaAggCUgJpZA=='); - -@$core.Deprecated('Use buildTaskUpdateDescriptor instead') -const BuildTaskUpdate$json = { - '1': 'BuildTaskUpdate', - '2': [ - {'1': 'build_id', '3': 1, '4': 1, '5': 9, '10': 'buildId'}, - {'1': 'task', '3': 2, '4': 1, '5': 11, '6': '.buildbucket.v2.Task', '10': 'task'}, - ], -}; - -/// Descriptor for `BuildTaskUpdate`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List buildTaskUpdateDescriptor = - $convert.base64Decode('Cg9CdWlsZFRhc2tVcGRhdGUSGQoIYnVpbGRfaWQYASABKAlSB2J1aWxkSWQSKAoEdGFzaxgCIA' - 'EoCzIULmJ1aWxkYnVja2V0LnYyLlRhc2tSBHRhc2s='); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/task.pbserver.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/task.pbserver.dart deleted file mode 100644 index 028d314cf..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/buildbucket/proto/task.pbserver.dart +++ /dev/null @@ -1,8 +0,0 @@ -/// -// Generated code. Do not modify. -// source: go.chromium.org/luci/buildbucket/proto/task.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 'task.pb.dart'; diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/common/proto/structmask/structmask.pb.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/common/proto/structmask/structmask.pb.dart deleted file mode 100644 index 9f018066a..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/common/proto/structmask/structmask.pb.dart +++ /dev/null @@ -1,54 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/common/proto/structmask/structmask.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:core' as $core; - -import 'package:protobuf/protobuf.dart' as $pb; - -class StructMask extends $pb.GeneratedMessage { - factory StructMask() => create(); - StructMask._() : super(); - factory StructMask.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory StructMask.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'StructMask', - package: const $pb.PackageName(_omitMessageNames ? '' : 'structmask'), createEmptyInstance: create) - ..pPS(1, _omitFieldNames ? '' : 'path') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - StructMask clone() => StructMask()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - StructMask copyWith(void Function(StructMask) updates) => - super.copyWith((message) => updates(message as StructMask)) as StructMask; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static StructMask create() => StructMask._(); - StructMask createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static StructMask getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static StructMask? _defaultInstance; - - @$pb.TagNumber(1) - $core.List<$core.String> get path => $_getList(0); -} - -const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); -const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/common/proto/structmask/structmask.pbenum.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/common/proto/structmask/structmask.pbenum.dart deleted file mode 100644 index 5344c4f10..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/common/proto/structmask/structmask.pbenum.dart +++ /dev/null @@ -1,10 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/common/proto/structmask/structmask.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/common/proto/structmask/structmask.pbjson.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/common/proto/structmask/structmask.pbjson.dart deleted file mode 100644 index a43f050fd..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/common/proto/structmask/structmask.pbjson.dart +++ /dev/null @@ -1,26 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/common/proto/structmask/structmask.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:convert' as $convert; -import 'dart:core' as $core; -import 'dart:typed_data' as $typed_data; - -@$core.Deprecated('Use structMaskDescriptor instead') -const StructMask$json = { - '1': 'StructMask', - '2': [ - {'1': 'path', '3': 1, '4': 3, '5': 9, '10': 'path'}, - ], -}; - -/// Descriptor for `StructMask`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List structMaskDescriptor = - $convert.base64Decode('CgpTdHJ1Y3RNYXNrEhIKBHBhdGgYASADKAlSBHBhdGg='); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/common.pb.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/common.pb.dart deleted file mode 100644 index 3479a1e68..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/common.pb.dart +++ /dev/null @@ -1,646 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/resultdb/proto/v1/common.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:core' as $core; - -import 'package:fixnum/fixnum.dart' as $fixnum; -import 'package:protobuf/protobuf.dart' as $pb; - -import '../../../../../google/protobuf/timestamp.pb.dart' as $0; - -class Variant extends $pb.GeneratedMessage { - factory Variant() => create(); - Variant._() : super(); - factory Variant.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory Variant.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Variant', - package: const $pb.PackageName(_omitMessageNames ? '' : 'luci.resultdb.v1'), createEmptyInstance: create) - ..m<$core.String, $core.String>(1, _omitFieldNames ? '' : 'def', - entryClassName: 'Variant.DefEntry', - keyFieldType: $pb.PbFieldType.OS, - valueFieldType: $pb.PbFieldType.OS, - packageName: const $pb.PackageName('luci.resultdb.v1')) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - Variant clone() => Variant()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - Variant copyWith(void Function(Variant) updates) => - super.copyWith((message) => updates(message as Variant)) as Variant; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static Variant create() => Variant._(); - Variant createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Variant getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Variant? _defaultInstance; - - @$pb.TagNumber(1) - $core.Map<$core.String, $core.String> get def => $_getMap(0); -} - -class StringPair extends $pb.GeneratedMessage { - factory StringPair() => create(); - StringPair._() : super(); - factory StringPair.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory StringPair.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'StringPair', - package: const $pb.PackageName(_omitMessageNames ? '' : 'luci.resultdb.v1'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'key') - ..aOS(2, _omitFieldNames ? '' : 'value') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - StringPair clone() => StringPair()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - StringPair copyWith(void Function(StringPair) updates) => - super.copyWith((message) => updates(message as StringPair)) as StringPair; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static StringPair create() => StringPair._(); - StringPair createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static StringPair getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static StringPair? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get key => $_getSZ(0); - @$pb.TagNumber(1) - set key($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasKey() => $_has(0); - @$pb.TagNumber(1) - void clearKey() => clearField(1); - - @$pb.TagNumber(2) - $core.String get value => $_getSZ(1); - @$pb.TagNumber(2) - set value($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasValue() => $_has(1); - @$pb.TagNumber(2) - void clearValue() => clearField(2); -} - -class GitilesCommit extends $pb.GeneratedMessage { - factory GitilesCommit() => create(); - GitilesCommit._() : super(); - factory GitilesCommit.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory GitilesCommit.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'GitilesCommit', - package: const $pb.PackageName(_omitMessageNames ? '' : 'luci.resultdb.v1'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'host') - ..aOS(2, _omitFieldNames ? '' : 'project') - ..aOS(3, _omitFieldNames ? '' : 'ref') - ..aOS(4, _omitFieldNames ? '' : 'commitHash') - ..aInt64(5, _omitFieldNames ? '' : 'position') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - GitilesCommit clone() => GitilesCommit()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - GitilesCommit copyWith(void Function(GitilesCommit) updates) => - super.copyWith((message) => updates(message as GitilesCommit)) as GitilesCommit; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static GitilesCommit create() => GitilesCommit._(); - GitilesCommit createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static GitilesCommit getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static GitilesCommit? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get host => $_getSZ(0); - @$pb.TagNumber(1) - set host($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasHost() => $_has(0); - @$pb.TagNumber(1) - void clearHost() => clearField(1); - - @$pb.TagNumber(2) - $core.String get project => $_getSZ(1); - @$pb.TagNumber(2) - set project($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasProject() => $_has(1); - @$pb.TagNumber(2) - void clearProject() => clearField(2); - - @$pb.TagNumber(3) - $core.String get ref => $_getSZ(2); - @$pb.TagNumber(3) - set ref($core.String v) { - $_setString(2, v); - } - - @$pb.TagNumber(3) - $core.bool hasRef() => $_has(2); - @$pb.TagNumber(3) - void clearRef() => clearField(3); - - @$pb.TagNumber(4) - $core.String get commitHash => $_getSZ(3); - @$pb.TagNumber(4) - set commitHash($core.String v) { - $_setString(3, v); - } - - @$pb.TagNumber(4) - $core.bool hasCommitHash() => $_has(3); - @$pb.TagNumber(4) - void clearCommitHash() => clearField(4); - - @$pb.TagNumber(5) - $fixnum.Int64 get position => $_getI64(4); - @$pb.TagNumber(5) - set position($fixnum.Int64 v) { - $_setInt64(4, v); - } - - @$pb.TagNumber(5) - $core.bool hasPosition() => $_has(4); - @$pb.TagNumber(5) - void clearPosition() => clearField(5); -} - -class GerritChange extends $pb.GeneratedMessage { - factory GerritChange() => create(); - GerritChange._() : super(); - factory GerritChange.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory GerritChange.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'GerritChange', - package: const $pb.PackageName(_omitMessageNames ? '' : 'luci.resultdb.v1'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'host') - ..aOS(2, _omitFieldNames ? '' : 'project') - ..aInt64(3, _omitFieldNames ? '' : 'change') - ..aInt64(4, _omitFieldNames ? '' : 'patchset') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - GerritChange clone() => GerritChange()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - GerritChange copyWith(void Function(GerritChange) updates) => - super.copyWith((message) => updates(message as GerritChange)) as GerritChange; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static GerritChange create() => GerritChange._(); - GerritChange createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static GerritChange getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static GerritChange? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get host => $_getSZ(0); - @$pb.TagNumber(1) - set host($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasHost() => $_has(0); - @$pb.TagNumber(1) - void clearHost() => clearField(1); - - @$pb.TagNumber(2) - $core.String get project => $_getSZ(1); - @$pb.TagNumber(2) - set project($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasProject() => $_has(1); - @$pb.TagNumber(2) - void clearProject() => clearField(2); - - @$pb.TagNumber(3) - $fixnum.Int64 get change => $_getI64(2); - @$pb.TagNumber(3) - set change($fixnum.Int64 v) { - $_setInt64(2, v); - } - - @$pb.TagNumber(3) - $core.bool hasChange() => $_has(2); - @$pb.TagNumber(3) - void clearChange() => clearField(3); - - @$pb.TagNumber(4) - $fixnum.Int64 get patchset => $_getI64(3); - @$pb.TagNumber(4) - set patchset($fixnum.Int64 v) { - $_setInt64(3, v); - } - - @$pb.TagNumber(4) - $core.bool hasPatchset() => $_has(3); - @$pb.TagNumber(4) - void clearPatchset() => clearField(4); -} - -class CommitPosition extends $pb.GeneratedMessage { - factory CommitPosition() => create(); - CommitPosition._() : super(); - factory CommitPosition.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory CommitPosition.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'CommitPosition', - package: const $pb.PackageName(_omitMessageNames ? '' : 'luci.resultdb.v1'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'host') - ..aOS(2, _omitFieldNames ? '' : 'project') - ..aOS(3, _omitFieldNames ? '' : 'ref') - ..aInt64(4, _omitFieldNames ? '' : 'position') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - CommitPosition clone() => CommitPosition()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - CommitPosition copyWith(void Function(CommitPosition) updates) => - super.copyWith((message) => updates(message as CommitPosition)) as CommitPosition; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static CommitPosition create() => CommitPosition._(); - CommitPosition createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static CommitPosition getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static CommitPosition? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get host => $_getSZ(0); - @$pb.TagNumber(1) - set host($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasHost() => $_has(0); - @$pb.TagNumber(1) - void clearHost() => clearField(1); - - @$pb.TagNumber(2) - $core.String get project => $_getSZ(1); - @$pb.TagNumber(2) - set project($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasProject() => $_has(1); - @$pb.TagNumber(2) - void clearProject() => clearField(2); - - @$pb.TagNumber(3) - $core.String get ref => $_getSZ(2); - @$pb.TagNumber(3) - set ref($core.String v) { - $_setString(2, v); - } - - @$pb.TagNumber(3) - $core.bool hasRef() => $_has(2); - @$pb.TagNumber(3) - void clearRef() => clearField(3); - - @$pb.TagNumber(4) - $fixnum.Int64 get position => $_getI64(3); - @$pb.TagNumber(4) - set position($fixnum.Int64 v) { - $_setInt64(3, v); - } - - @$pb.TagNumber(4) - $core.bool hasPosition() => $_has(3); - @$pb.TagNumber(4) - void clearPosition() => clearField(4); -} - -class CommitPositionRange extends $pb.GeneratedMessage { - factory CommitPositionRange() => create(); - CommitPositionRange._() : super(); - factory CommitPositionRange.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory CommitPositionRange.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'CommitPositionRange', - package: const $pb.PackageName(_omitMessageNames ? '' : 'luci.resultdb.v1'), createEmptyInstance: create) - ..aOM(1, _omitFieldNames ? '' : 'earliest', subBuilder: CommitPosition.create) - ..aOM(2, _omitFieldNames ? '' : 'latest', subBuilder: CommitPosition.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - CommitPositionRange clone() => CommitPositionRange()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - CommitPositionRange copyWith(void Function(CommitPositionRange) updates) => - super.copyWith((message) => updates(message as CommitPositionRange)) as CommitPositionRange; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static CommitPositionRange create() => CommitPositionRange._(); - CommitPositionRange createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static CommitPositionRange getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static CommitPositionRange? _defaultInstance; - - @$pb.TagNumber(1) - CommitPosition get earliest => $_getN(0); - @$pb.TagNumber(1) - set earliest(CommitPosition v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasEarliest() => $_has(0); - @$pb.TagNumber(1) - void clearEarliest() => clearField(1); - @$pb.TagNumber(1) - CommitPosition ensureEarliest() => $_ensure(0); - - @$pb.TagNumber(2) - CommitPosition get latest => $_getN(1); - @$pb.TagNumber(2) - set latest(CommitPosition v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasLatest() => $_has(1); - @$pb.TagNumber(2) - void clearLatest() => clearField(2); - @$pb.TagNumber(2) - CommitPosition ensureLatest() => $_ensure(1); -} - -class TimeRange extends $pb.GeneratedMessage { - factory TimeRange() => create(); - TimeRange._() : super(); - factory TimeRange.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory TimeRange.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'TimeRange', - package: const $pb.PackageName(_omitMessageNames ? '' : 'luci.resultdb.v1'), createEmptyInstance: create) - ..aOM<$0.Timestamp>(1, _omitFieldNames ? '' : 'earliest', subBuilder: $0.Timestamp.create) - ..aOM<$0.Timestamp>(2, _omitFieldNames ? '' : 'latest', subBuilder: $0.Timestamp.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - TimeRange clone() => TimeRange()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - TimeRange copyWith(void Function(TimeRange) updates) => - super.copyWith((message) => updates(message as TimeRange)) as TimeRange; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static TimeRange create() => TimeRange._(); - TimeRange createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static TimeRange getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static TimeRange? _defaultInstance; - - @$pb.TagNumber(1) - $0.Timestamp get earliest => $_getN(0); - @$pb.TagNumber(1) - set earliest($0.Timestamp v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasEarliest() => $_has(0); - @$pb.TagNumber(1) - void clearEarliest() => clearField(1); - @$pb.TagNumber(1) - $0.Timestamp ensureEarliest() => $_ensure(0); - - @$pb.TagNumber(2) - $0.Timestamp get latest => $_getN(1); - @$pb.TagNumber(2) - set latest($0.Timestamp v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasLatest() => $_has(1); - @$pb.TagNumber(2) - void clearLatest() => clearField(2); - @$pb.TagNumber(2) - $0.Timestamp ensureLatest() => $_ensure(1); -} - -enum SourceRef_System { gitiles, notSet } - -class SourceRef extends $pb.GeneratedMessage { - factory SourceRef() => create(); - SourceRef._() : super(); - factory SourceRef.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory SourceRef.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static const $core.Map<$core.int, SourceRef_System> _SourceRef_SystemByTag = { - 1: SourceRef_System.gitiles, - 0: SourceRef_System.notSet - }; - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'SourceRef', - package: const $pb.PackageName(_omitMessageNames ? '' : 'luci.resultdb.v1'), createEmptyInstance: create) - ..oo(0, [1]) - ..aOM(1, _omitFieldNames ? '' : 'gitiles', subBuilder: GitilesRef.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - SourceRef clone() => SourceRef()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - SourceRef copyWith(void Function(SourceRef) updates) => - super.copyWith((message) => updates(message as SourceRef)) as SourceRef; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static SourceRef create() => SourceRef._(); - SourceRef createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static SourceRef getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static SourceRef? _defaultInstance; - - SourceRef_System whichSystem() => _SourceRef_SystemByTag[$_whichOneof(0)]!; - void clearSystem() => clearField($_whichOneof(0)); - - @$pb.TagNumber(1) - GitilesRef get gitiles => $_getN(0); - @$pb.TagNumber(1) - set gitiles(GitilesRef v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasGitiles() => $_has(0); - @$pb.TagNumber(1) - void clearGitiles() => clearField(1); - @$pb.TagNumber(1) - GitilesRef ensureGitiles() => $_ensure(0); -} - -class GitilesRef extends $pb.GeneratedMessage { - factory GitilesRef() => create(); - GitilesRef._() : super(); - factory GitilesRef.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory GitilesRef.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'GitilesRef', - package: const $pb.PackageName(_omitMessageNames ? '' : 'luci.resultdb.v1'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'host') - ..aOS(2, _omitFieldNames ? '' : 'project') - ..aOS(3, _omitFieldNames ? '' : 'ref') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - GitilesRef clone() => GitilesRef()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - GitilesRef copyWith(void Function(GitilesRef) updates) => - super.copyWith((message) => updates(message as GitilesRef)) as GitilesRef; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static GitilesRef create() => GitilesRef._(); - GitilesRef createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static GitilesRef getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static GitilesRef? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get host => $_getSZ(0); - @$pb.TagNumber(1) - set host($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasHost() => $_has(0); - @$pb.TagNumber(1) - void clearHost() => clearField(1); - - @$pb.TagNumber(2) - $core.String get project => $_getSZ(1); - @$pb.TagNumber(2) - set project($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasProject() => $_has(1); - @$pb.TagNumber(2) - void clearProject() => clearField(2); - - @$pb.TagNumber(3) - $core.String get ref => $_getSZ(2); - @$pb.TagNumber(3) - set ref($core.String v) { - $_setString(2, v); - } - - @$pb.TagNumber(3) - $core.bool hasRef() => $_has(2); - @$pb.TagNumber(3) - void clearRef() => clearField(3); -} - -const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); -const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/common.pbenum.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/common.pbenum.dart deleted file mode 100644 index f6e78526d..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/common.pbenum.dart +++ /dev/null @@ -1,10 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/resultdb/proto/v1/common.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/common.pbjson.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/common.pbjson.dart deleted file mode 100644 index 0b37ba301..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/common.pbjson.dart +++ /dev/null @@ -1,164 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/resultdb/proto/v1/common.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:convert' as $convert; -import 'dart:core' as $core; -import 'dart:typed_data' as $typed_data; - -@$core.Deprecated('Use variantDescriptor instead') -const Variant$json = { - '1': 'Variant', - '2': [ - {'1': 'def', '3': 1, '4': 3, '5': 11, '6': '.luci.resultdb.v1.Variant.DefEntry', '10': 'def'}, - ], - '3': [Variant_DefEntry$json], -}; - -@$core.Deprecated('Use variantDescriptor instead') -const Variant_DefEntry$json = { - '1': 'DefEntry', - '2': [ - {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, - {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'}, - ], - '7': {'7': true}, -}; - -/// Descriptor for `Variant`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List variantDescriptor = - $convert.base64Decode('CgdWYXJpYW50EjQKA2RlZhgBIAMoCzIiLmx1Y2kucmVzdWx0ZGIudjEuVmFyaWFudC5EZWZFbn' - 'RyeVIDZGVmGjYKCERlZkVudHJ5EhAKA2tleRgBIAEoCVIDa2V5EhQKBXZhbHVlGAIgASgJUgV2' - 'YWx1ZToCOAE='); - -@$core.Deprecated('Use stringPairDescriptor instead') -const StringPair$json = { - '1': 'StringPair', - '2': [ - {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, - {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'}, - ], -}; - -/// Descriptor for `StringPair`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List stringPairDescriptor = - $convert.base64Decode('CgpTdHJpbmdQYWlyEhAKA2tleRgBIAEoCVIDa2V5EhQKBXZhbHVlGAIgASgJUgV2YWx1ZQ=='); - -@$core.Deprecated('Use gitilesCommitDescriptor instead') -const GitilesCommit$json = { - '1': 'GitilesCommit', - '2': [ - {'1': 'host', '3': 1, '4': 1, '5': 9, '10': 'host'}, - {'1': 'project', '3': 2, '4': 1, '5': 9, '10': 'project'}, - {'1': 'ref', '3': 3, '4': 1, '5': 9, '10': 'ref'}, - {'1': 'commit_hash', '3': 4, '4': 1, '5': 9, '10': 'commitHash'}, - {'1': 'position', '3': 5, '4': 1, '5': 3, '10': 'position'}, - ], -}; - -/// Descriptor for `GitilesCommit`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List gitilesCommitDescriptor = - $convert.base64Decode('Cg1HaXRpbGVzQ29tbWl0EhIKBGhvc3QYASABKAlSBGhvc3QSGAoHcHJvamVjdBgCIAEoCVIHcH' - 'JvamVjdBIQCgNyZWYYAyABKAlSA3JlZhIfCgtjb21taXRfaGFzaBgEIAEoCVIKY29tbWl0SGFz' - 'aBIaCghwb3NpdGlvbhgFIAEoA1IIcG9zaXRpb24='); - -@$core.Deprecated('Use gerritChangeDescriptor instead') -const GerritChange$json = { - '1': 'GerritChange', - '2': [ - {'1': 'host', '3': 1, '4': 1, '5': 9, '10': 'host'}, - {'1': 'project', '3': 2, '4': 1, '5': 9, '10': 'project'}, - {'1': 'change', '3': 3, '4': 1, '5': 3, '10': 'change'}, - {'1': 'patchset', '3': 4, '4': 1, '5': 3, '10': 'patchset'}, - ], -}; - -/// Descriptor for `GerritChange`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List gerritChangeDescriptor = - $convert.base64Decode('CgxHZXJyaXRDaGFuZ2USEgoEaG9zdBgBIAEoCVIEaG9zdBIYCgdwcm9qZWN0GAIgASgJUgdwcm' - '9qZWN0EhYKBmNoYW5nZRgDIAEoA1IGY2hhbmdlEhoKCHBhdGNoc2V0GAQgASgDUghwYXRjaHNl' - 'dA=='); - -@$core.Deprecated('Use commitPositionDescriptor instead') -const CommitPosition$json = { - '1': 'CommitPosition', - '2': [ - {'1': 'host', '3': 1, '4': 1, '5': 9, '10': 'host'}, - {'1': 'project', '3': 2, '4': 1, '5': 9, '10': 'project'}, - {'1': 'ref', '3': 3, '4': 1, '5': 9, '10': 'ref'}, - {'1': 'position', '3': 4, '4': 1, '5': 3, '10': 'position'}, - ], -}; - -/// Descriptor for `CommitPosition`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List commitPositionDescriptor = - $convert.base64Decode('Cg5Db21taXRQb3NpdGlvbhISCgRob3N0GAEgASgJUgRob3N0EhgKB3Byb2plY3QYAiABKAlSB3' - 'Byb2plY3QSEAoDcmVmGAMgASgJUgNyZWYSGgoIcG9zaXRpb24YBCABKANSCHBvc2l0aW9u'); - -@$core.Deprecated('Use commitPositionRangeDescriptor instead') -const CommitPositionRange$json = { - '1': 'CommitPositionRange', - '2': [ - {'1': 'earliest', '3': 1, '4': 1, '5': 11, '6': '.luci.resultdb.v1.CommitPosition', '10': 'earliest'}, - {'1': 'latest', '3': 2, '4': 1, '5': 11, '6': '.luci.resultdb.v1.CommitPosition', '10': 'latest'}, - ], -}; - -/// Descriptor for `CommitPositionRange`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List commitPositionRangeDescriptor = - $convert.base64Decode('ChNDb21taXRQb3NpdGlvblJhbmdlEjwKCGVhcmxpZXN0GAEgASgLMiAubHVjaS5yZXN1bHRkYi' - '52MS5Db21taXRQb3NpdGlvblIIZWFybGllc3QSOAoGbGF0ZXN0GAIgASgLMiAubHVjaS5yZXN1' - 'bHRkYi52MS5Db21taXRQb3NpdGlvblIGbGF0ZXN0'); - -@$core.Deprecated('Use timeRangeDescriptor instead') -const TimeRange$json = { - '1': 'TimeRange', - '2': [ - {'1': 'earliest', '3': 1, '4': 1, '5': 11, '6': '.google.protobuf.Timestamp', '10': 'earliest'}, - {'1': 'latest', '3': 2, '4': 1, '5': 11, '6': '.google.protobuf.Timestamp', '10': 'latest'}, - ], -}; - -/// Descriptor for `TimeRange`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List timeRangeDescriptor = - $convert.base64Decode('CglUaW1lUmFuZ2USNgoIZWFybGllc3QYASABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW' - '1wUghlYXJsaWVzdBIyCgZsYXRlc3QYAiABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1w' - 'UgZsYXRlc3Q='); - -@$core.Deprecated('Use sourceRefDescriptor instead') -const SourceRef$json = { - '1': 'SourceRef', - '2': [ - {'1': 'gitiles', '3': 1, '4': 1, '5': 11, '6': '.luci.resultdb.v1.GitilesRef', '9': 0, '10': 'gitiles'}, - ], - '8': [ - {'1': 'system'}, - ], -}; - -/// Descriptor for `SourceRef`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List sourceRefDescriptor = - $convert.base64Decode('CglTb3VyY2VSZWYSOAoHZ2l0aWxlcxgBIAEoCzIcLmx1Y2kucmVzdWx0ZGIudjEuR2l0aWxlc1' - 'JlZkgAUgdnaXRpbGVzQggKBnN5c3RlbQ=='); - -@$core.Deprecated('Use gitilesRefDescriptor instead') -const GitilesRef$json = { - '1': 'GitilesRef', - '2': [ - {'1': 'host', '3': 1, '4': 1, '5': 9, '10': 'host'}, - {'1': 'project', '3': 2, '4': 1, '5': 9, '10': 'project'}, - {'1': 'ref', '3': 3, '4': 1, '5': 9, '10': 'ref'}, - ], -}; - -/// Descriptor for `GitilesRef`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List gitilesRefDescriptor = - $convert.base64Decode('CgpHaXRpbGVzUmVmEhIKBGhvc3QYASABKAlSBGhvc3QSGAoHcHJvamVjdBgCIAEoCVIHcHJvam' - 'VjdBIQCgNyZWYYAyABKAlSA3JlZg=='); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/common.pbserver.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/common.pbserver.dart deleted file mode 100644 index 693893bf3..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/common.pbserver.dart +++ /dev/null @@ -1,8 +0,0 @@ -/// -// Generated code. Do not modify. -// source: go.chromium.org/luci/resultdb/proto/v1/common.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 'common.pb.dart'; diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/invocation.pb.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/invocation.pb.dart deleted file mode 100644 index bb3581630..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/invocation.pb.dart +++ /dev/null @@ -1,647 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/resultdb/proto/v1/invocation.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:core' as $core; - -import 'package:protobuf/protobuf.dart' as $pb; - -import '../../../../../google/protobuf/struct.pb.dart' as $2; -import '../../../../../google/protobuf/timestamp.pb.dart' as $0; -import 'common.pb.dart' as $1; -import 'invocation.pbenum.dart'; -import 'predicate.pb.dart' as $3; - -export 'invocation.pbenum.dart'; - -class Invocation extends $pb.GeneratedMessage { - factory Invocation() => create(); - Invocation._() : super(); - factory Invocation.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory Invocation.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Invocation', - package: const $pb.PackageName(_omitMessageNames ? '' : 'luci.resultdb.v1'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'name') - ..e(2, _omitFieldNames ? '' : 'state', $pb.PbFieldType.OE, - defaultOrMaker: Invocation_State.STATE_UNSPECIFIED, - valueOf: Invocation_State.valueOf, - enumValues: Invocation_State.values) - ..aOM<$0.Timestamp>(4, _omitFieldNames ? '' : 'createTime', subBuilder: $0.Timestamp.create) - ..pc<$1.StringPair>(5, _omitFieldNames ? '' : 'tags', $pb.PbFieldType.PM, subBuilder: $1.StringPair.create) - ..aOM<$0.Timestamp>(6, _omitFieldNames ? '' : 'finalizeTime', subBuilder: $0.Timestamp.create) - ..aOM<$0.Timestamp>(7, _omitFieldNames ? '' : 'deadline', subBuilder: $0.Timestamp.create) - ..pPS(8, _omitFieldNames ? '' : 'includedInvocations') - ..pc(9, _omitFieldNames ? '' : 'bigqueryExports', $pb.PbFieldType.PM, - subBuilder: BigQueryExport.create) - ..aOS(10, _omitFieldNames ? '' : 'createdBy') - ..aOS(11, _omitFieldNames ? '' : 'producerResource') - ..aOS(12, _omitFieldNames ? '' : 'realm') - ..aOM(13, _omitFieldNames ? '' : 'historyOptions', subBuilder: HistoryOptions.create) - ..aOM<$2.Struct>(14, _omitFieldNames ? '' : 'properties', subBuilder: $2.Struct.create) - ..aOM(15, _omitFieldNames ? '' : 'sourceSpec', subBuilder: SourceSpec.create) - ..aOS(16, _omitFieldNames ? '' : 'baselineId') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - Invocation clone() => Invocation()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - Invocation copyWith(void Function(Invocation) updates) => - super.copyWith((message) => updates(message as Invocation)) as Invocation; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static Invocation create() => Invocation._(); - Invocation createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Invocation getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Invocation? _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) - Invocation_State get state => $_getN(1); - @$pb.TagNumber(2) - set state(Invocation_State v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasState() => $_has(1); - @$pb.TagNumber(2) - void clearState() => clearField(2); - - @$pb.TagNumber(4) - $0.Timestamp get createTime => $_getN(2); - @$pb.TagNumber(4) - set createTime($0.Timestamp v) { - setField(4, v); - } - - @$pb.TagNumber(4) - $core.bool hasCreateTime() => $_has(2); - @$pb.TagNumber(4) - void clearCreateTime() => clearField(4); - @$pb.TagNumber(4) - $0.Timestamp ensureCreateTime() => $_ensure(2); - - @$pb.TagNumber(5) - $core.List<$1.StringPair> get tags => $_getList(3); - - @$pb.TagNumber(6) - $0.Timestamp get finalizeTime => $_getN(4); - @$pb.TagNumber(6) - set finalizeTime($0.Timestamp v) { - setField(6, v); - } - - @$pb.TagNumber(6) - $core.bool hasFinalizeTime() => $_has(4); - @$pb.TagNumber(6) - void clearFinalizeTime() => clearField(6); - @$pb.TagNumber(6) - $0.Timestamp ensureFinalizeTime() => $_ensure(4); - - @$pb.TagNumber(7) - $0.Timestamp get deadline => $_getN(5); - @$pb.TagNumber(7) - set deadline($0.Timestamp v) { - setField(7, v); - } - - @$pb.TagNumber(7) - $core.bool hasDeadline() => $_has(5); - @$pb.TagNumber(7) - void clearDeadline() => clearField(7); - @$pb.TagNumber(7) - $0.Timestamp ensureDeadline() => $_ensure(5); - - @$pb.TagNumber(8) - $core.List<$core.String> get includedInvocations => $_getList(6); - - @$pb.TagNumber(9) - $core.List get bigqueryExports => $_getList(7); - - @$pb.TagNumber(10) - $core.String get createdBy => $_getSZ(8); - @$pb.TagNumber(10) - set createdBy($core.String v) { - $_setString(8, v); - } - - @$pb.TagNumber(10) - $core.bool hasCreatedBy() => $_has(8); - @$pb.TagNumber(10) - void clearCreatedBy() => clearField(10); - - @$pb.TagNumber(11) - $core.String get producerResource => $_getSZ(9); - @$pb.TagNumber(11) - set producerResource($core.String v) { - $_setString(9, v); - } - - @$pb.TagNumber(11) - $core.bool hasProducerResource() => $_has(9); - @$pb.TagNumber(11) - void clearProducerResource() => clearField(11); - - @$pb.TagNumber(12) - $core.String get realm => $_getSZ(10); - @$pb.TagNumber(12) - set realm($core.String v) { - $_setString(10, v); - } - - @$pb.TagNumber(12) - $core.bool hasRealm() => $_has(10); - @$pb.TagNumber(12) - void clearRealm() => clearField(12); - - @$pb.TagNumber(13) - HistoryOptions get historyOptions => $_getN(11); - @$pb.TagNumber(13) - set historyOptions(HistoryOptions v) { - setField(13, v); - } - - @$pb.TagNumber(13) - $core.bool hasHistoryOptions() => $_has(11); - @$pb.TagNumber(13) - void clearHistoryOptions() => clearField(13); - @$pb.TagNumber(13) - HistoryOptions ensureHistoryOptions() => $_ensure(11); - - @$pb.TagNumber(14) - $2.Struct get properties => $_getN(12); - @$pb.TagNumber(14) - set properties($2.Struct v) { - setField(14, v); - } - - @$pb.TagNumber(14) - $core.bool hasProperties() => $_has(12); - @$pb.TagNumber(14) - void clearProperties() => clearField(14); - @$pb.TagNumber(14) - $2.Struct ensureProperties() => $_ensure(12); - - @$pb.TagNumber(15) - SourceSpec get sourceSpec => $_getN(13); - @$pb.TagNumber(15) - set sourceSpec(SourceSpec v) { - setField(15, v); - } - - @$pb.TagNumber(15) - $core.bool hasSourceSpec() => $_has(13); - @$pb.TagNumber(15) - void clearSourceSpec() => clearField(15); - @$pb.TagNumber(15) - SourceSpec ensureSourceSpec() => $_ensure(13); - - @$pb.TagNumber(16) - $core.String get baselineId => $_getSZ(14); - @$pb.TagNumber(16) - set baselineId($core.String v) { - $_setString(14, v); - } - - @$pb.TagNumber(16) - $core.bool hasBaselineId() => $_has(14); - @$pb.TagNumber(16) - void clearBaselineId() => clearField(16); -} - -class BigQueryExport_TestResults extends $pb.GeneratedMessage { - factory BigQueryExport_TestResults() => create(); - BigQueryExport_TestResults._() : super(); - factory BigQueryExport_TestResults.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BigQueryExport_TestResults.fromJson($core.String i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BigQueryExport.TestResults', - package: const $pb.PackageName(_omitMessageNames ? '' : 'luci.resultdb.v1'), createEmptyInstance: create) - ..aOM<$3.TestResultPredicate>(1, _omitFieldNames ? '' : 'predicate', subBuilder: $3.TestResultPredicate.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BigQueryExport_TestResults clone() => BigQueryExport_TestResults()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BigQueryExport_TestResults copyWith(void Function(BigQueryExport_TestResults) updates) => - super.copyWith((message) => updates(message as BigQueryExport_TestResults)) as BigQueryExport_TestResults; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BigQueryExport_TestResults create() => BigQueryExport_TestResults._(); - BigQueryExport_TestResults createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BigQueryExport_TestResults getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BigQueryExport_TestResults? _defaultInstance; - - @$pb.TagNumber(1) - $3.TestResultPredicate get predicate => $_getN(0); - @$pb.TagNumber(1) - set predicate($3.TestResultPredicate v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasPredicate() => $_has(0); - @$pb.TagNumber(1) - void clearPredicate() => clearField(1); - @$pb.TagNumber(1) - $3.TestResultPredicate ensurePredicate() => $_ensure(0); -} - -class BigQueryExport_TextArtifacts extends $pb.GeneratedMessage { - factory BigQueryExport_TextArtifacts() => create(); - BigQueryExport_TextArtifacts._() : super(); - factory BigQueryExport_TextArtifacts.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BigQueryExport_TextArtifacts.fromJson($core.String i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BigQueryExport.TextArtifacts', - package: const $pb.PackageName(_omitMessageNames ? '' : 'luci.resultdb.v1'), createEmptyInstance: create) - ..aOM<$3.ArtifactPredicate>(1, _omitFieldNames ? '' : 'predicate', subBuilder: $3.ArtifactPredicate.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BigQueryExport_TextArtifacts clone() => BigQueryExport_TextArtifacts()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BigQueryExport_TextArtifacts copyWith(void Function(BigQueryExport_TextArtifacts) updates) => - super.copyWith((message) => updates(message as BigQueryExport_TextArtifacts)) as BigQueryExport_TextArtifacts; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BigQueryExport_TextArtifacts create() => BigQueryExport_TextArtifacts._(); - BigQueryExport_TextArtifacts createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BigQueryExport_TextArtifacts getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BigQueryExport_TextArtifacts? _defaultInstance; - - @$pb.TagNumber(1) - $3.ArtifactPredicate get predicate => $_getN(0); - @$pb.TagNumber(1) - set predicate($3.ArtifactPredicate v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasPredicate() => $_has(0); - @$pb.TagNumber(1) - void clearPredicate() => clearField(1); - @$pb.TagNumber(1) - $3.ArtifactPredicate ensurePredicate() => $_ensure(0); -} - -enum BigQueryExport_ResultType { testResults, textArtifacts, notSet } - -class BigQueryExport extends $pb.GeneratedMessage { - factory BigQueryExport() => create(); - BigQueryExport._() : super(); - factory BigQueryExport.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BigQueryExport.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static const $core.Map<$core.int, BigQueryExport_ResultType> _BigQueryExport_ResultTypeByTag = { - 4: BigQueryExport_ResultType.testResults, - 6: BigQueryExport_ResultType.textArtifacts, - 0: BigQueryExport_ResultType.notSet - }; - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BigQueryExport', - package: const $pb.PackageName(_omitMessageNames ? '' : 'luci.resultdb.v1'), createEmptyInstance: create) - ..oo(0, [4, 6]) - ..aOS(1, _omitFieldNames ? '' : 'project') - ..aOS(2, _omitFieldNames ? '' : 'dataset') - ..aOS(3, _omitFieldNames ? '' : 'table') - ..aOM(4, _omitFieldNames ? '' : 'testResults', - subBuilder: BigQueryExport_TestResults.create) - ..aOM(6, _omitFieldNames ? '' : 'textArtifacts', - subBuilder: BigQueryExport_TextArtifacts.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BigQueryExport clone() => BigQueryExport()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BigQueryExport copyWith(void Function(BigQueryExport) updates) => - super.copyWith((message) => updates(message as BigQueryExport)) as BigQueryExport; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BigQueryExport create() => BigQueryExport._(); - BigQueryExport createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BigQueryExport getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BigQueryExport? _defaultInstance; - - BigQueryExport_ResultType whichResultType() => _BigQueryExport_ResultTypeByTag[$_whichOneof(0)]!; - void clearResultType() => clearField($_whichOneof(0)); - - @$pb.TagNumber(1) - $core.String get project => $_getSZ(0); - @$pb.TagNumber(1) - set project($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasProject() => $_has(0); - @$pb.TagNumber(1) - void clearProject() => clearField(1); - - @$pb.TagNumber(2) - $core.String get dataset => $_getSZ(1); - @$pb.TagNumber(2) - set dataset($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasDataset() => $_has(1); - @$pb.TagNumber(2) - void clearDataset() => clearField(2); - - @$pb.TagNumber(3) - $core.String get table => $_getSZ(2); - @$pb.TagNumber(3) - set table($core.String v) { - $_setString(2, v); - } - - @$pb.TagNumber(3) - $core.bool hasTable() => $_has(2); - @$pb.TagNumber(3) - void clearTable() => clearField(3); - - @$pb.TagNumber(4) - BigQueryExport_TestResults get testResults => $_getN(3); - @$pb.TagNumber(4) - set testResults(BigQueryExport_TestResults v) { - setField(4, v); - } - - @$pb.TagNumber(4) - $core.bool hasTestResults() => $_has(3); - @$pb.TagNumber(4) - void clearTestResults() => clearField(4); - @$pb.TagNumber(4) - BigQueryExport_TestResults ensureTestResults() => $_ensure(3); - - @$pb.TagNumber(6) - BigQueryExport_TextArtifacts get textArtifacts => $_getN(4); - @$pb.TagNumber(6) - set textArtifacts(BigQueryExport_TextArtifacts v) { - setField(6, v); - } - - @$pb.TagNumber(6) - $core.bool hasTextArtifacts() => $_has(4); - @$pb.TagNumber(6) - void clearTextArtifacts() => clearField(6); - @$pb.TagNumber(6) - BigQueryExport_TextArtifacts ensureTextArtifacts() => $_ensure(4); -} - -class HistoryOptions extends $pb.GeneratedMessage { - factory HistoryOptions() => create(); - HistoryOptions._() : super(); - factory HistoryOptions.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory HistoryOptions.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'HistoryOptions', - package: const $pb.PackageName(_omitMessageNames ? '' : 'luci.resultdb.v1'), createEmptyInstance: create) - ..aOB(1, _omitFieldNames ? '' : 'useInvocationTimestamp') - ..aOM<$1.CommitPosition>(2, _omitFieldNames ? '' : 'commit', subBuilder: $1.CommitPosition.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - HistoryOptions clone() => HistoryOptions()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - HistoryOptions copyWith(void Function(HistoryOptions) updates) => - super.copyWith((message) => updates(message as HistoryOptions)) as HistoryOptions; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static HistoryOptions create() => HistoryOptions._(); - HistoryOptions createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static HistoryOptions getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static HistoryOptions? _defaultInstance; - - @$pb.TagNumber(1) - $core.bool get useInvocationTimestamp => $_getBF(0); - @$pb.TagNumber(1) - set useInvocationTimestamp($core.bool v) { - $_setBool(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasUseInvocationTimestamp() => $_has(0); - @$pb.TagNumber(1) - void clearUseInvocationTimestamp() => clearField(1); - - @$pb.TagNumber(2) - $1.CommitPosition get commit => $_getN(1); - @$pb.TagNumber(2) - set commit($1.CommitPosition v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasCommit() => $_has(1); - @$pb.TagNumber(2) - void clearCommit() => clearField(2); - @$pb.TagNumber(2) - $1.CommitPosition ensureCommit() => $_ensure(1); -} - -class SourceSpec extends $pb.GeneratedMessage { - factory SourceSpec() => create(); - SourceSpec._() : super(); - factory SourceSpec.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory SourceSpec.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'SourceSpec', - package: const $pb.PackageName(_omitMessageNames ? '' : 'luci.resultdb.v1'), createEmptyInstance: create) - ..aOM(1, _omitFieldNames ? '' : 'sources', subBuilder: Sources.create) - ..aOB(2, _omitFieldNames ? '' : 'inherit') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - SourceSpec clone() => SourceSpec()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - SourceSpec copyWith(void Function(SourceSpec) updates) => - super.copyWith((message) => updates(message as SourceSpec)) as SourceSpec; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static SourceSpec create() => SourceSpec._(); - SourceSpec createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static SourceSpec getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static SourceSpec? _defaultInstance; - - @$pb.TagNumber(1) - Sources get sources => $_getN(0); - @$pb.TagNumber(1) - set sources(Sources v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasSources() => $_has(0); - @$pb.TagNumber(1) - void clearSources() => clearField(1); - @$pb.TagNumber(1) - Sources ensureSources() => $_ensure(0); - - @$pb.TagNumber(2) - $core.bool get inherit => $_getBF(1); - @$pb.TagNumber(2) - set inherit($core.bool v) { - $_setBool(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasInherit() => $_has(1); - @$pb.TagNumber(2) - void clearInherit() => clearField(2); -} - -class Sources extends $pb.GeneratedMessage { - factory Sources() => create(); - Sources._() : super(); - factory Sources.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory Sources.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Sources', - package: const $pb.PackageName(_omitMessageNames ? '' : 'luci.resultdb.v1'), createEmptyInstance: create) - ..aOM<$1.GitilesCommit>(1, _omitFieldNames ? '' : 'gitilesCommit', subBuilder: $1.GitilesCommit.create) - ..pc<$1.GerritChange>(2, _omitFieldNames ? '' : 'changelists', $pb.PbFieldType.PM, - subBuilder: $1.GerritChange.create) - ..aOB(3, _omitFieldNames ? '' : 'isDirty') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - Sources clone() => Sources()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - Sources copyWith(void Function(Sources) updates) => - super.copyWith((message) => updates(message as Sources)) as Sources; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static Sources create() => Sources._(); - Sources createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Sources getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Sources? _defaultInstance; - - @$pb.TagNumber(1) - $1.GitilesCommit get gitilesCommit => $_getN(0); - @$pb.TagNumber(1) - set gitilesCommit($1.GitilesCommit v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasGitilesCommit() => $_has(0); - @$pb.TagNumber(1) - void clearGitilesCommit() => clearField(1); - @$pb.TagNumber(1) - $1.GitilesCommit ensureGitilesCommit() => $_ensure(0); - - @$pb.TagNumber(2) - $core.List<$1.GerritChange> get changelists => $_getList(1); - - @$pb.TagNumber(3) - $core.bool get isDirty => $_getBF(2); - @$pb.TagNumber(3) - set isDirty($core.bool v) { - $_setBool(2, v); - } - - @$pb.TagNumber(3) - $core.bool hasIsDirty() => $_has(2); - @$pb.TagNumber(3) - void clearIsDirty() => clearField(3); -} - -const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); -const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/invocation.pbenum.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/invocation.pbenum.dart deleted file mode 100644 index 32688e020..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/invocation.pbenum.dart +++ /dev/null @@ -1,35 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/resultdb/proto/v1/invocation.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:core' as $core; - -import 'package:protobuf/protobuf.dart' as $pb; - -class Invocation_State extends $pb.ProtobufEnum { - static const Invocation_State STATE_UNSPECIFIED = Invocation_State._(0, _omitEnumNames ? '' : 'STATE_UNSPECIFIED'); - static const Invocation_State ACTIVE = Invocation_State._(1, _omitEnumNames ? '' : 'ACTIVE'); - static const Invocation_State FINALIZING = Invocation_State._(2, _omitEnumNames ? '' : 'FINALIZING'); - static const Invocation_State FINALIZED = Invocation_State._(3, _omitEnumNames ? '' : 'FINALIZED'); - - static const $core.List values = [ - STATE_UNSPECIFIED, - ACTIVE, - FINALIZING, - FINALIZED, - ]; - - static final $core.Map<$core.int, Invocation_State> _byValue = $pb.ProtobufEnum.initByValue(values); - static Invocation_State? valueOf($core.int value) => _byValue[value]; - - const Invocation_State._($core.int v, $core.String n) : super(v, n); -} - -const _omitEnumNames = $core.bool.fromEnvironment('protobuf.omit_enum_names'); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/invocation.pbjson.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/invocation.pbjson.dart deleted file mode 100644 index 07c32a45a..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/invocation.pbjson.dart +++ /dev/null @@ -1,183 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/resultdb/proto/v1/invocation.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:convert' as $convert; -import 'dart:core' as $core; -import 'dart:typed_data' as $typed_data; - -@$core.Deprecated('Use invocationDescriptor instead') -const Invocation$json = { - '1': 'Invocation', - '2': [ - {'1': 'name', '3': 1, '4': 1, '5': 9, '8': {}, '10': 'name'}, - {'1': 'state', '3': 2, '4': 1, '5': 14, '6': '.luci.resultdb.v1.Invocation.State', '10': 'state'}, - {'1': 'create_time', '3': 4, '4': 1, '5': 11, '6': '.google.protobuf.Timestamp', '8': {}, '10': 'createTime'}, - {'1': 'tags', '3': 5, '4': 3, '5': 11, '6': '.luci.resultdb.v1.StringPair', '10': 'tags'}, - {'1': 'finalize_time', '3': 6, '4': 1, '5': 11, '6': '.google.protobuf.Timestamp', '8': {}, '10': 'finalizeTime'}, - {'1': 'deadline', '3': 7, '4': 1, '5': 11, '6': '.google.protobuf.Timestamp', '10': 'deadline'}, - {'1': 'included_invocations', '3': 8, '4': 3, '5': 9, '10': 'includedInvocations'}, - { - '1': 'bigquery_exports', - '3': 9, - '4': 3, - '5': 11, - '6': '.luci.resultdb.v1.BigQueryExport', - '10': 'bigqueryExports' - }, - {'1': 'created_by', '3': 10, '4': 1, '5': 9, '8': {}, '10': 'createdBy'}, - {'1': 'producer_resource', '3': 11, '4': 1, '5': 9, '10': 'producerResource'}, - {'1': 'realm', '3': 12, '4': 1, '5': 9, '10': 'realm'}, - {'1': 'history_options', '3': 13, '4': 1, '5': 11, '6': '.luci.resultdb.v1.HistoryOptions', '10': 'historyOptions'}, - {'1': 'properties', '3': 14, '4': 1, '5': 11, '6': '.google.protobuf.Struct', '10': 'properties'}, - {'1': 'source_spec', '3': 15, '4': 1, '5': 11, '6': '.luci.resultdb.v1.SourceSpec', '10': 'sourceSpec'}, - {'1': 'baseline_id', '3': 16, '4': 1, '5': 9, '10': 'baselineId'}, - ], - '4': [Invocation_State$json], - '9': [ - {'1': 3, '2': 4}, - ], -}; - -@$core.Deprecated('Use invocationDescriptor instead') -const Invocation_State$json = { - '1': 'State', - '2': [ - {'1': 'STATE_UNSPECIFIED', '2': 0}, - {'1': 'ACTIVE', '2': 1}, - {'1': 'FINALIZING', '2': 2}, - {'1': 'FINALIZED', '2': 3}, - ], -}; - -/// Descriptor for `Invocation`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List invocationDescriptor = - $convert.base64Decode('CgpJbnZvY2F0aW9uEhoKBG5hbWUYASABKAlCBuBBA+BBBVIEbmFtZRI4CgVzdGF0ZRgCIAEoDj' - 'IiLmx1Y2kucmVzdWx0ZGIudjEuSW52b2NhdGlvbi5TdGF0ZVIFc3RhdGUSQwoLY3JlYXRlX3Rp' - 'bWUYBCABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wQgbgQQPgQQVSCmNyZWF0ZVRpbW' - 'USMAoEdGFncxgFIAMoCzIcLmx1Y2kucmVzdWx0ZGIudjEuU3RyaW5nUGFpclIEdGFncxJECg1m' - 'aW5hbGl6ZV90aW1lGAYgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcEID4EEDUgxmaW' - '5hbGl6ZVRpbWUSNgoIZGVhZGxpbmUYByABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1w' - 'UghkZWFkbGluZRIxChRpbmNsdWRlZF9pbnZvY2F0aW9ucxgIIAMoCVITaW5jbHVkZWRJbnZvY2' - 'F0aW9ucxJLChBiaWdxdWVyeV9leHBvcnRzGAkgAygLMiAubHVjaS5yZXN1bHRkYi52MS5CaWdR' - 'dWVyeUV4cG9ydFIPYmlncXVlcnlFeHBvcnRzEiIKCmNyZWF0ZWRfYnkYCiABKAlCA+BBA1IJY3' - 'JlYXRlZEJ5EisKEXByb2R1Y2VyX3Jlc291cmNlGAsgASgJUhBwcm9kdWNlclJlc291cmNlEhQK' - 'BXJlYWxtGAwgASgJUgVyZWFsbRJJCg9oaXN0b3J5X29wdGlvbnMYDSABKAsyIC5sdWNpLnJlc3' - 'VsdGRiLnYxLkhpc3RvcnlPcHRpb25zUg5oaXN0b3J5T3B0aW9ucxI3Cgpwcm9wZXJ0aWVzGA4g' - 'ASgLMhcuZ29vZ2xlLnByb3RvYnVmLlN0cnVjdFIKcHJvcGVydGllcxI9Cgtzb3VyY2Vfc3BlYx' - 'gPIAEoCzIcLmx1Y2kucmVzdWx0ZGIudjEuU291cmNlU3BlY1IKc291cmNlU3BlYxIfCgtiYXNl' - 'bGluZV9pZBgQIAEoCVIKYmFzZWxpbmVJZCJJCgVTdGF0ZRIVChFTVEFURV9VTlNQRUNJRklFRB' - 'AAEgoKBkFDVElWRRABEg4KCkZJTkFMSVpJTkcQAhINCglGSU5BTElaRUQQA0oECAMQBA=='); - -@$core.Deprecated('Use bigQueryExportDescriptor instead') -const BigQueryExport$json = { - '1': 'BigQueryExport', - '2': [ - {'1': 'project', '3': 1, '4': 1, '5': 9, '8': {}, '10': 'project'}, - {'1': 'dataset', '3': 2, '4': 1, '5': 9, '8': {}, '10': 'dataset'}, - {'1': 'table', '3': 3, '4': 1, '5': 9, '8': {}, '10': 'table'}, - { - '1': 'test_results', - '3': 4, - '4': 1, - '5': 11, - '6': '.luci.resultdb.v1.BigQueryExport.TestResults', - '9': 0, - '10': 'testResults' - }, - { - '1': 'text_artifacts', - '3': 6, - '4': 1, - '5': 11, - '6': '.luci.resultdb.v1.BigQueryExport.TextArtifacts', - '9': 0, - '10': 'textArtifacts' - }, - ], - '3': [BigQueryExport_TestResults$json, BigQueryExport_TextArtifacts$json], - '8': [ - {'1': 'result_type'}, - ], -}; - -@$core.Deprecated('Use bigQueryExportDescriptor instead') -const BigQueryExport_TestResults$json = { - '1': 'TestResults', - '2': [ - {'1': 'predicate', '3': 1, '4': 1, '5': 11, '6': '.luci.resultdb.v1.TestResultPredicate', '10': 'predicate'}, - ], -}; - -@$core.Deprecated('Use bigQueryExportDescriptor instead') -const BigQueryExport_TextArtifacts$json = { - '1': 'TextArtifacts', - '2': [ - {'1': 'predicate', '3': 1, '4': 1, '5': 11, '6': '.luci.resultdb.v1.ArtifactPredicate', '10': 'predicate'}, - ], -}; - -/// Descriptor for `BigQueryExport`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List bigQueryExportDescriptor = - $convert.base64Decode('Cg5CaWdRdWVyeUV4cG9ydBIdCgdwcm9qZWN0GAEgASgJQgPgQQJSB3Byb2plY3QSHQoHZGF0YX' - 'NldBgCIAEoCUID4EECUgdkYXRhc2V0EhkKBXRhYmxlGAMgASgJQgPgQQJSBXRhYmxlElEKDHRl' - 'c3RfcmVzdWx0cxgEIAEoCzIsLmx1Y2kucmVzdWx0ZGIudjEuQmlnUXVlcnlFeHBvcnQuVGVzdF' - 'Jlc3VsdHNIAFILdGVzdFJlc3VsdHMSVwoOdGV4dF9hcnRpZmFjdHMYBiABKAsyLi5sdWNpLnJl' - 'c3VsdGRiLnYxLkJpZ1F1ZXJ5RXhwb3J0LlRleHRBcnRpZmFjdHNIAFINdGV4dEFydGlmYWN0cx' - 'pSCgtUZXN0UmVzdWx0cxJDCglwcmVkaWNhdGUYASABKAsyJS5sdWNpLnJlc3VsdGRiLnYxLlRl' - 'c3RSZXN1bHRQcmVkaWNhdGVSCXByZWRpY2F0ZRpSCg1UZXh0QXJ0aWZhY3RzEkEKCXByZWRpY2' - 'F0ZRgBIAEoCzIjLmx1Y2kucmVzdWx0ZGIudjEuQXJ0aWZhY3RQcmVkaWNhdGVSCXByZWRpY2F0' - 'ZUINCgtyZXN1bHRfdHlwZQ=='); - -@$core.Deprecated('Use historyOptionsDescriptor instead') -const HistoryOptions$json = { - '1': 'HistoryOptions', - '2': [ - {'1': 'use_invocation_timestamp', '3': 1, '4': 1, '5': 8, '10': 'useInvocationTimestamp'}, - {'1': 'commit', '3': 2, '4': 1, '5': 11, '6': '.luci.resultdb.v1.CommitPosition', '10': 'commit'}, - ], -}; - -/// Descriptor for `HistoryOptions`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List historyOptionsDescriptor = - $convert.base64Decode('Cg5IaXN0b3J5T3B0aW9ucxI4Chh1c2VfaW52b2NhdGlvbl90aW1lc3RhbXAYASABKAhSFnVzZU' - 'ludm9jYXRpb25UaW1lc3RhbXASOAoGY29tbWl0GAIgASgLMiAubHVjaS5yZXN1bHRkYi52MS5D' - 'b21taXRQb3NpdGlvblIGY29tbWl0'); - -@$core.Deprecated('Use sourceSpecDescriptor instead') -const SourceSpec$json = { - '1': 'SourceSpec', - '2': [ - {'1': 'sources', '3': 1, '4': 1, '5': 11, '6': '.luci.resultdb.v1.Sources', '10': 'sources'}, - {'1': 'inherit', '3': 2, '4': 1, '5': 8, '10': 'inherit'}, - ], -}; - -/// Descriptor for `SourceSpec`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List sourceSpecDescriptor = - $convert.base64Decode('CgpTb3VyY2VTcGVjEjMKB3NvdXJjZXMYASABKAsyGS5sdWNpLnJlc3VsdGRiLnYxLlNvdXJjZX' - 'NSB3NvdXJjZXMSGAoHaW5oZXJpdBgCIAEoCFIHaW5oZXJpdA=='); - -@$core.Deprecated('Use sourcesDescriptor instead') -const Sources$json = { - '1': 'Sources', - '2': [ - {'1': 'gitiles_commit', '3': 1, '4': 1, '5': 11, '6': '.luci.resultdb.v1.GitilesCommit', '10': 'gitilesCommit'}, - {'1': 'changelists', '3': 2, '4': 3, '5': 11, '6': '.luci.resultdb.v1.GerritChange', '10': 'changelists'}, - {'1': 'is_dirty', '3': 3, '4': 1, '5': 8, '10': 'isDirty'}, - ], -}; - -/// Descriptor for `Sources`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List sourcesDescriptor = - $convert.base64Decode('CgdTb3VyY2VzEkYKDmdpdGlsZXNfY29tbWl0GAEgASgLMh8ubHVjaS5yZXN1bHRkYi52MS5HaX' - 'RpbGVzQ29tbWl0Ug1naXRpbGVzQ29tbWl0EkAKC2NoYW5nZWxpc3RzGAIgAygLMh4ubHVjaS5y' - 'ZXN1bHRkYi52MS5HZXJyaXRDaGFuZ2VSC2NoYW5nZWxpc3RzEhkKCGlzX2RpcnR5GAMgASgIUg' - 'dpc0RpcnR5'); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/invocation.pbserver.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/invocation.pbserver.dart deleted file mode 100644 index ee26ba049..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/invocation.pbserver.dart +++ /dev/null @@ -1,8 +0,0 @@ -/// -// Generated code. Do not modify. -// source: go.chromium.org/luci/resultdb/proto/v1/invocation.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 'invocation.pb.dart'; diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/predicate.pb.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/predicate.pb.dart deleted file mode 100644 index 9f7331c84..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/predicate.pb.dart +++ /dev/null @@ -1,446 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/resultdb/proto/v1/predicate.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:core' as $core; - -import 'package:protobuf/protobuf.dart' as $pb; - -import 'common.pb.dart' as $0; -import 'predicate.pbenum.dart'; - -export 'predicate.pbenum.dart'; - -class TestResultPredicate extends $pb.GeneratedMessage { - factory TestResultPredicate() => create(); - TestResultPredicate._() : super(); - factory TestResultPredicate.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory TestResultPredicate.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'TestResultPredicate', - package: const $pb.PackageName(_omitMessageNames ? '' : 'luci.resultdb.v1'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'testIdRegexp') - ..aOM(2, _omitFieldNames ? '' : 'variant', subBuilder: VariantPredicate.create) - ..e(3, _omitFieldNames ? '' : 'expectancy', $pb.PbFieldType.OE, - defaultOrMaker: TestResultPredicate_Expectancy.ALL, - valueOf: TestResultPredicate_Expectancy.valueOf, - enumValues: TestResultPredicate_Expectancy.values) - ..aOB(4, _omitFieldNames ? '' : 'excludeExonerated') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - TestResultPredicate clone() => TestResultPredicate()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - TestResultPredicate copyWith(void Function(TestResultPredicate) updates) => - super.copyWith((message) => updates(message as TestResultPredicate)) as TestResultPredicate; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static TestResultPredicate create() => TestResultPredicate._(); - TestResultPredicate createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static TestResultPredicate getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static TestResultPredicate? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get testIdRegexp => $_getSZ(0); - @$pb.TagNumber(1) - set testIdRegexp($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasTestIdRegexp() => $_has(0); - @$pb.TagNumber(1) - void clearTestIdRegexp() => clearField(1); - - @$pb.TagNumber(2) - VariantPredicate get variant => $_getN(1); - @$pb.TagNumber(2) - set variant(VariantPredicate v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasVariant() => $_has(1); - @$pb.TagNumber(2) - void clearVariant() => clearField(2); - @$pb.TagNumber(2) - VariantPredicate ensureVariant() => $_ensure(1); - - @$pb.TagNumber(3) - TestResultPredicate_Expectancy get expectancy => $_getN(2); - @$pb.TagNumber(3) - set expectancy(TestResultPredicate_Expectancy v) { - setField(3, v); - } - - @$pb.TagNumber(3) - $core.bool hasExpectancy() => $_has(2); - @$pb.TagNumber(3) - void clearExpectancy() => clearField(3); - - @$pb.TagNumber(4) - $core.bool get excludeExonerated => $_getBF(3); - @$pb.TagNumber(4) - set excludeExonerated($core.bool v) { - $_setBool(3, v); - } - - @$pb.TagNumber(4) - $core.bool hasExcludeExonerated() => $_has(3); - @$pb.TagNumber(4) - void clearExcludeExonerated() => clearField(4); -} - -class TestExonerationPredicate extends $pb.GeneratedMessage { - factory TestExonerationPredicate() => create(); - TestExonerationPredicate._() : super(); - factory TestExonerationPredicate.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory TestExonerationPredicate.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'TestExonerationPredicate', - package: const $pb.PackageName(_omitMessageNames ? '' : 'luci.resultdb.v1'), createEmptyInstance: create) - ..aOS(1, _omitFieldNames ? '' : 'testIdRegexp') - ..aOM(2, _omitFieldNames ? '' : 'variant', subBuilder: VariantPredicate.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - TestExonerationPredicate clone() => TestExonerationPredicate()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - TestExonerationPredicate copyWith(void Function(TestExonerationPredicate) updates) => - super.copyWith((message) => updates(message as TestExonerationPredicate)) as TestExonerationPredicate; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static TestExonerationPredicate create() => TestExonerationPredicate._(); - TestExonerationPredicate createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static TestExonerationPredicate getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static TestExonerationPredicate? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get testIdRegexp => $_getSZ(0); - @$pb.TagNumber(1) - set testIdRegexp($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasTestIdRegexp() => $_has(0); - @$pb.TagNumber(1) - void clearTestIdRegexp() => clearField(1); - - @$pb.TagNumber(2) - VariantPredicate get variant => $_getN(1); - @$pb.TagNumber(2) - set variant(VariantPredicate v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasVariant() => $_has(1); - @$pb.TagNumber(2) - void clearVariant() => clearField(2); - @$pb.TagNumber(2) - VariantPredicate ensureVariant() => $_ensure(1); -} - -enum VariantPredicate_Predicate { equals, contains, notSet } - -class VariantPredicate extends $pb.GeneratedMessage { - factory VariantPredicate() => create(); - VariantPredicate._() : super(); - factory VariantPredicate.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory VariantPredicate.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static const $core.Map<$core.int, VariantPredicate_Predicate> _VariantPredicate_PredicateByTag = { - 1: VariantPredicate_Predicate.equals, - 2: VariantPredicate_Predicate.contains, - 0: VariantPredicate_Predicate.notSet - }; - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'VariantPredicate', - package: const $pb.PackageName(_omitMessageNames ? '' : 'luci.resultdb.v1'), createEmptyInstance: create) - ..oo(0, [1, 2]) - ..aOM<$0.Variant>(1, _omitFieldNames ? '' : 'equals', subBuilder: $0.Variant.create) - ..aOM<$0.Variant>(2, _omitFieldNames ? '' : 'contains', subBuilder: $0.Variant.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - VariantPredicate clone() => VariantPredicate()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - VariantPredicate copyWith(void Function(VariantPredicate) updates) => - super.copyWith((message) => updates(message as VariantPredicate)) as VariantPredicate; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static VariantPredicate create() => VariantPredicate._(); - VariantPredicate createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static VariantPredicate getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static VariantPredicate? _defaultInstance; - - VariantPredicate_Predicate whichPredicate() => _VariantPredicate_PredicateByTag[$_whichOneof(0)]!; - void clearPredicate() => clearField($_whichOneof(0)); - - @$pb.TagNumber(1) - $0.Variant get equals => $_getN(0); - @$pb.TagNumber(1) - set equals($0.Variant v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasEquals() => $_has(0); - @$pb.TagNumber(1) - void clearEquals() => clearField(1); - @$pb.TagNumber(1) - $0.Variant ensureEquals() => $_ensure(0); - - @$pb.TagNumber(2) - $0.Variant get contains => $_getN(1); - @$pb.TagNumber(2) - set contains($0.Variant v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasContains() => $_has(1); - @$pb.TagNumber(2) - void clearContains() => clearField(2); - @$pb.TagNumber(2) - $0.Variant ensureContains() => $_ensure(1); -} - -class ArtifactPredicate_EdgeTypeSet extends $pb.GeneratedMessage { - factory ArtifactPredicate_EdgeTypeSet() => create(); - ArtifactPredicate_EdgeTypeSet._() : super(); - factory ArtifactPredicate_EdgeTypeSet.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory ArtifactPredicate_EdgeTypeSet.fromJson($core.String i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ArtifactPredicate.EdgeTypeSet', - package: const $pb.PackageName(_omitMessageNames ? '' : 'luci.resultdb.v1'), createEmptyInstance: create) - ..aOB(1, _omitFieldNames ? '' : 'includedInvocations') - ..aOB(2, _omitFieldNames ? '' : 'testResults') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - ArtifactPredicate_EdgeTypeSet clone() => ArtifactPredicate_EdgeTypeSet()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - ArtifactPredicate_EdgeTypeSet copyWith(void Function(ArtifactPredicate_EdgeTypeSet) updates) => - super.copyWith((message) => updates(message as ArtifactPredicate_EdgeTypeSet)) as ArtifactPredicate_EdgeTypeSet; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static ArtifactPredicate_EdgeTypeSet create() => ArtifactPredicate_EdgeTypeSet._(); - ArtifactPredicate_EdgeTypeSet createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static ArtifactPredicate_EdgeTypeSet getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static ArtifactPredicate_EdgeTypeSet? _defaultInstance; - - @$pb.TagNumber(1) - $core.bool get includedInvocations => $_getBF(0); - @$pb.TagNumber(1) - set includedInvocations($core.bool v) { - $_setBool(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasIncludedInvocations() => $_has(0); - @$pb.TagNumber(1) - void clearIncludedInvocations() => clearField(1); - - @$pb.TagNumber(2) - $core.bool get testResults => $_getBF(1); - @$pb.TagNumber(2) - set testResults($core.bool v) { - $_setBool(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasTestResults() => $_has(1); - @$pb.TagNumber(2) - void clearTestResults() => clearField(2); -} - -class ArtifactPredicate extends $pb.GeneratedMessage { - factory ArtifactPredicate() => create(); - ArtifactPredicate._() : super(); - factory ArtifactPredicate.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory ArtifactPredicate.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ArtifactPredicate', - package: const $pb.PackageName(_omitMessageNames ? '' : 'luci.resultdb.v1'), createEmptyInstance: create) - ..aOM(1, _omitFieldNames ? '' : 'followEdges', - subBuilder: ArtifactPredicate_EdgeTypeSet.create) - ..aOM(2, _omitFieldNames ? '' : 'testResultPredicate', subBuilder: TestResultPredicate.create) - ..aOS(3, _omitFieldNames ? '' : 'contentTypeRegexp') - ..aOS(4, _omitFieldNames ? '' : 'artifactIdRegexp') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - ArtifactPredicate clone() => ArtifactPredicate()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - ArtifactPredicate copyWith(void Function(ArtifactPredicate) updates) => - super.copyWith((message) => updates(message as ArtifactPredicate)) as ArtifactPredicate; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static ArtifactPredicate create() => ArtifactPredicate._(); - ArtifactPredicate createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static ArtifactPredicate getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static ArtifactPredicate? _defaultInstance; - - @$pb.TagNumber(1) - ArtifactPredicate_EdgeTypeSet get followEdges => $_getN(0); - @$pb.TagNumber(1) - set followEdges(ArtifactPredicate_EdgeTypeSet v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasFollowEdges() => $_has(0); - @$pb.TagNumber(1) - void clearFollowEdges() => clearField(1); - @$pb.TagNumber(1) - ArtifactPredicate_EdgeTypeSet ensureFollowEdges() => $_ensure(0); - - @$pb.TagNumber(2) - TestResultPredicate get testResultPredicate => $_getN(1); - @$pb.TagNumber(2) - set testResultPredicate(TestResultPredicate v) { - setField(2, v); - } - - @$pb.TagNumber(2) - $core.bool hasTestResultPredicate() => $_has(1); - @$pb.TagNumber(2) - void clearTestResultPredicate() => clearField(2); - @$pb.TagNumber(2) - TestResultPredicate ensureTestResultPredicate() => $_ensure(1); - - @$pb.TagNumber(3) - $core.String get contentTypeRegexp => $_getSZ(2); - @$pb.TagNumber(3) - set contentTypeRegexp($core.String v) { - $_setString(2, v); - } - - @$pb.TagNumber(3) - $core.bool hasContentTypeRegexp() => $_has(2); - @$pb.TagNumber(3) - void clearContentTypeRegexp() => clearField(3); - - @$pb.TagNumber(4) - $core.String get artifactIdRegexp => $_getSZ(3); - @$pb.TagNumber(4) - set artifactIdRegexp($core.String v) { - $_setString(3, v); - } - - @$pb.TagNumber(4) - $core.bool hasArtifactIdRegexp() => $_has(3); - @$pb.TagNumber(4) - void clearArtifactIdRegexp() => clearField(4); -} - -class TestMetadataPredicate extends $pb.GeneratedMessage { - factory TestMetadataPredicate() => create(); - TestMetadataPredicate._() : super(); - factory TestMetadataPredicate.fromBuffer($core.List<$core.int> i, - [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory TestMetadataPredicate.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'TestMetadataPredicate', - package: const $pb.PackageName(_omitMessageNames ? '' : 'luci.resultdb.v1'), createEmptyInstance: create) - ..pPS(1, _omitFieldNames ? '' : 'testIds') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - TestMetadataPredicate clone() => TestMetadataPredicate()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - TestMetadataPredicate copyWith(void Function(TestMetadataPredicate) updates) => - super.copyWith((message) => updates(message as TestMetadataPredicate)) as TestMetadataPredicate; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static TestMetadataPredicate create() => TestMetadataPredicate._(); - TestMetadataPredicate createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static TestMetadataPredicate getDefault() => - _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static TestMetadataPredicate? _defaultInstance; - - @$pb.TagNumber(1) - $core.List<$core.String> get testIds => $_getList(0); -} - -const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); -const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/predicate.pbenum.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/predicate.pbenum.dart deleted file mode 100644 index b650e9276..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/predicate.pbenum.dart +++ /dev/null @@ -1,35 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/resultdb/proto/v1/predicate.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:core' as $core; - -import 'package:protobuf/protobuf.dart' as $pb; - -class TestResultPredicate_Expectancy extends $pb.ProtobufEnum { - static const TestResultPredicate_Expectancy ALL = TestResultPredicate_Expectancy._(0, _omitEnumNames ? '' : 'ALL'); - static const TestResultPredicate_Expectancy VARIANTS_WITH_UNEXPECTED_RESULTS = - TestResultPredicate_Expectancy._(1, _omitEnumNames ? '' : 'VARIANTS_WITH_UNEXPECTED_RESULTS'); - static const TestResultPredicate_Expectancy VARIANTS_WITH_ONLY_UNEXPECTED_RESULTS = - TestResultPredicate_Expectancy._(2, _omitEnumNames ? '' : 'VARIANTS_WITH_ONLY_UNEXPECTED_RESULTS'); - - static const $core.List values = [ - ALL, - VARIANTS_WITH_UNEXPECTED_RESULTS, - VARIANTS_WITH_ONLY_UNEXPECTED_RESULTS, - ]; - - static final $core.Map<$core.int, TestResultPredicate_Expectancy> _byValue = $pb.ProtobufEnum.initByValue(values); - static TestResultPredicate_Expectancy? valueOf($core.int value) => _byValue[value]; - - const TestResultPredicate_Expectancy._($core.int v, $core.String n) : super(v, n); -} - -const _omitEnumNames = $core.bool.fromEnvironment('protobuf.omit_enum_names'); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/predicate.pbjson.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/predicate.pbjson.dart deleted file mode 100644 index 46406e294..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/predicate.pbjson.dart +++ /dev/null @@ -1,144 +0,0 @@ -// -// Generated code. Do not modify. -// source: go.chromium.org/luci/resultdb/proto/v1/predicate.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:convert' as $convert; -import 'dart:core' as $core; -import 'dart:typed_data' as $typed_data; - -@$core.Deprecated('Use testResultPredicateDescriptor instead') -const TestResultPredicate$json = { - '1': 'TestResultPredicate', - '2': [ - {'1': 'test_id_regexp', '3': 1, '4': 1, '5': 9, '10': 'testIdRegexp'}, - {'1': 'variant', '3': 2, '4': 1, '5': 11, '6': '.luci.resultdb.v1.VariantPredicate', '10': 'variant'}, - { - '1': 'expectancy', - '3': 3, - '4': 1, - '5': 14, - '6': '.luci.resultdb.v1.TestResultPredicate.Expectancy', - '10': 'expectancy' - }, - {'1': 'exclude_exonerated', '3': 4, '4': 1, '5': 8, '10': 'excludeExonerated'}, - ], - '4': [TestResultPredicate_Expectancy$json], -}; - -@$core.Deprecated('Use testResultPredicateDescriptor instead') -const TestResultPredicate_Expectancy$json = { - '1': 'Expectancy', - '2': [ - {'1': 'ALL', '2': 0}, - {'1': 'VARIANTS_WITH_UNEXPECTED_RESULTS', '2': 1}, - {'1': 'VARIANTS_WITH_ONLY_UNEXPECTED_RESULTS', '2': 2}, - ], -}; - -/// Descriptor for `TestResultPredicate`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List testResultPredicateDescriptor = - $convert.base64Decode('ChNUZXN0UmVzdWx0UHJlZGljYXRlEiQKDnRlc3RfaWRfcmVnZXhwGAEgASgJUgx0ZXN0SWRSZW' - 'dleHASPAoHdmFyaWFudBgCIAEoCzIiLmx1Y2kucmVzdWx0ZGIudjEuVmFyaWFudFByZWRpY2F0' - 'ZVIHdmFyaWFudBJQCgpleHBlY3RhbmN5GAMgASgOMjAubHVjaS5yZXN1bHRkYi52MS5UZXN0Um' - 'VzdWx0UHJlZGljYXRlLkV4cGVjdGFuY3lSCmV4cGVjdGFuY3kSLQoSZXhjbHVkZV9leG9uZXJh' - 'dGVkGAQgASgIUhFleGNsdWRlRXhvbmVyYXRlZCJmCgpFeHBlY3RhbmN5EgcKA0FMTBAAEiQKIF' - 'ZBUklBTlRTX1dJVEhfVU5FWFBFQ1RFRF9SRVNVTFRTEAESKQolVkFSSUFOVFNfV0lUSF9PTkxZ' - 'X1VORVhQRUNURURfUkVTVUxUUxAC'); - -@$core.Deprecated('Use testExonerationPredicateDescriptor instead') -const TestExonerationPredicate$json = { - '1': 'TestExonerationPredicate', - '2': [ - {'1': 'test_id_regexp', '3': 1, '4': 1, '5': 9, '10': 'testIdRegexp'}, - {'1': 'variant', '3': 2, '4': 1, '5': 11, '6': '.luci.resultdb.v1.VariantPredicate', '10': 'variant'}, - ], -}; - -/// Descriptor for `TestExonerationPredicate`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List testExonerationPredicateDescriptor = - $convert.base64Decode('ChhUZXN0RXhvbmVyYXRpb25QcmVkaWNhdGUSJAoOdGVzdF9pZF9yZWdleHAYASABKAlSDHRlc3' - 'RJZFJlZ2V4cBI8Cgd2YXJpYW50GAIgASgLMiIubHVjaS5yZXN1bHRkYi52MS5WYXJpYW50UHJl' - 'ZGljYXRlUgd2YXJpYW50'); - -@$core.Deprecated('Use variantPredicateDescriptor instead') -const VariantPredicate$json = { - '1': 'VariantPredicate', - '2': [ - {'1': 'equals', '3': 1, '4': 1, '5': 11, '6': '.luci.resultdb.v1.Variant', '9': 0, '10': 'equals'}, - {'1': 'contains', '3': 2, '4': 1, '5': 11, '6': '.luci.resultdb.v1.Variant', '9': 0, '10': 'contains'}, - ], - '8': [ - {'1': 'predicate'}, - ], -}; - -/// Descriptor for `VariantPredicate`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List variantPredicateDescriptor = - $convert.base64Decode('ChBWYXJpYW50UHJlZGljYXRlEjMKBmVxdWFscxgBIAEoCzIZLmx1Y2kucmVzdWx0ZGIudjEuVm' - 'FyaWFudEgAUgZlcXVhbHMSNwoIY29udGFpbnMYAiABKAsyGS5sdWNpLnJlc3VsdGRiLnYxLlZh' - 'cmlhbnRIAFIIY29udGFpbnNCCwoJcHJlZGljYXRl'); - -@$core.Deprecated('Use artifactPredicateDescriptor instead') -const ArtifactPredicate$json = { - '1': 'ArtifactPredicate', - '2': [ - { - '1': 'follow_edges', - '3': 1, - '4': 1, - '5': 11, - '6': '.luci.resultdb.v1.ArtifactPredicate.EdgeTypeSet', - '10': 'followEdges' - }, - { - '1': 'test_result_predicate', - '3': 2, - '4': 1, - '5': 11, - '6': '.luci.resultdb.v1.TestResultPredicate', - '10': 'testResultPredicate' - }, - {'1': 'content_type_regexp', '3': 3, '4': 1, '5': 9, '10': 'contentTypeRegexp'}, - {'1': 'artifact_id_regexp', '3': 4, '4': 1, '5': 9, '10': 'artifactIdRegexp'}, - ], - '3': [ArtifactPredicate_EdgeTypeSet$json], -}; - -@$core.Deprecated('Use artifactPredicateDescriptor instead') -const ArtifactPredicate_EdgeTypeSet$json = { - '1': 'EdgeTypeSet', - '2': [ - {'1': 'included_invocations', '3': 1, '4': 1, '5': 8, '10': 'includedInvocations'}, - {'1': 'test_results', '3': 2, '4': 1, '5': 8, '10': 'testResults'}, - ], -}; - -/// Descriptor for `ArtifactPredicate`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List artifactPredicateDescriptor = - $convert.base64Decode('ChFBcnRpZmFjdFByZWRpY2F0ZRJSCgxmb2xsb3dfZWRnZXMYASABKAsyLy5sdWNpLnJlc3VsdG' - 'RiLnYxLkFydGlmYWN0UHJlZGljYXRlLkVkZ2VUeXBlU2V0Ugtmb2xsb3dFZGdlcxJZChV0ZXN0' - 'X3Jlc3VsdF9wcmVkaWNhdGUYAiABKAsyJS5sdWNpLnJlc3VsdGRiLnYxLlRlc3RSZXN1bHRQcm' - 'VkaWNhdGVSE3Rlc3RSZXN1bHRQcmVkaWNhdGUSLgoTY29udGVudF90eXBlX3JlZ2V4cBgDIAEo' - 'CVIRY29udGVudFR5cGVSZWdleHASLAoSYXJ0aWZhY3RfaWRfcmVnZXhwGAQgASgJUhBhcnRpZm' - 'FjdElkUmVnZXhwGmMKC0VkZ2VUeXBlU2V0EjEKFGluY2x1ZGVkX2ludm9jYXRpb25zGAEgASgI' - 'UhNpbmNsdWRlZEludm9jYXRpb25zEiEKDHRlc3RfcmVzdWx0cxgCIAEoCFILdGVzdFJlc3VsdH' - 'M='); - -@$core.Deprecated('Use testMetadataPredicateDescriptor instead') -const TestMetadataPredicate$json = { - '1': 'TestMetadataPredicate', - '2': [ - {'1': 'test_ids', '3': 1, '4': 3, '5': 9, '10': 'testIds'}, - ], -}; - -/// Descriptor for `TestMetadataPredicate`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List testMetadataPredicateDescriptor = - $convert.base64Decode('ChVUZXN0TWV0YWRhdGFQcmVkaWNhdGUSGQoIdGVzdF9pZHMYASADKAlSB3Rlc3RJZHM='); diff --git a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/predicate.pbserver.dart b/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/predicate.pbserver.dart deleted file mode 100644 index 0b72f6a86..000000000 --- a/packages/buildbucket-dart/lib/src/generated/go.chromium.org/luci/resultdb/proto/v1/predicate.pbserver.dart +++ /dev/null @@ -1,8 +0,0 @@ -/// -// Generated code. Do not modify. -// source: go.chromium.org/luci/resultdb/proto/v1/predicate.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 'predicate.pb.dart'; diff --git a/packages/buildbucket-dart/lib/src/generated/google/protobuf/any.pb.dart b/packages/buildbucket-dart/lib/src/generated/google/protobuf/any.pb.dart deleted file mode 100644 index 1d8f01fc2..000000000 --- a/packages/buildbucket-dart/lib/src/generated/google/protobuf/any.pb.dart +++ /dev/null @@ -1,89 +0,0 @@ -// -// Generated code. Do not modify. -// source: google/protobuf/any.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:core' as $core; - -import 'package:protobuf/protobuf.dart' as $pb; -import 'package:protobuf/src/protobuf/mixins/well_known.dart' as $mixin; - -class Any extends $pb.GeneratedMessage with $mixin.AnyMixin { - factory Any() => create(); - Any._() : super(); - factory Any.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory Any.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Any', - package: const $pb.PackageName(_omitMessageNames ? '' : 'google.protobuf'), - createEmptyInstance: create, - toProto3Json: $mixin.AnyMixin.toProto3JsonHelper, - fromProto3Json: $mixin.AnyMixin.fromProto3JsonHelper) - ..aOS(1, _omitFieldNames ? '' : 'typeUrl') - ..a<$core.List<$core.int>>(2, _omitFieldNames ? '' : 'value', $pb.PbFieldType.OY) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - Any clone() => Any()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - Any copyWith(void Function(Any) updates) => super.copyWith((message) => updates(message as Any)) as Any; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static Any create() => Any._(); - Any createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Any getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Any? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get typeUrl => $_getSZ(0); - @$pb.TagNumber(1) - set typeUrl($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasTypeUrl() => $_has(0); - @$pb.TagNumber(1) - void clearTypeUrl() => clearField(1); - - @$pb.TagNumber(2) - $core.List<$core.int> get value => $_getN(1); - @$pb.TagNumber(2) - set value($core.List<$core.int> v) { - $_setBytes(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasValue() => $_has(1); - @$pb.TagNumber(2) - void clearValue() => clearField(2); - - /// Creates a new [Any] encoding [message]. - /// - /// The [typeUrl] will be [typeUrlPrefix]/`fullName` where `fullName` is - /// the fully qualified name of the type of [message]. - static Any pack($pb.GeneratedMessage message, {$core.String typeUrlPrefix = 'type.googleapis.com'}) { - final result = create(); - $mixin.AnyMixin.packIntoAny(result, message, typeUrlPrefix: typeUrlPrefix); - return result; - } -} - -const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); -const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/packages/buildbucket-dart/lib/src/generated/google/protobuf/any.pbenum.dart b/packages/buildbucket-dart/lib/src/generated/google/protobuf/any.pbenum.dart deleted file mode 100644 index 30bca8e14..000000000 --- a/packages/buildbucket-dart/lib/src/generated/google/protobuf/any.pbenum.dart +++ /dev/null @@ -1,10 +0,0 @@ -// -// Generated code. Do not modify. -// source: google/protobuf/any.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import diff --git a/packages/buildbucket-dart/lib/src/generated/google/protobuf/any.pbjson.dart b/packages/buildbucket-dart/lib/src/generated/google/protobuf/any.pbjson.dart deleted file mode 100644 index b2cbe7da4..000000000 --- a/packages/buildbucket-dart/lib/src/generated/google/protobuf/any.pbjson.dart +++ /dev/null @@ -1,27 +0,0 @@ -// -// Generated code. Do not modify. -// source: google/protobuf/any.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:convert' as $convert; -import 'dart:core' as $core; -import 'dart:typed_data' as $typed_data; - -@$core.Deprecated('Use anyDescriptor instead') -const Any$json = { - '1': 'Any', - '2': [ - {'1': 'type_url', '3': 1, '4': 1, '5': 9, '10': 'typeUrl'}, - {'1': 'value', '3': 2, '4': 1, '5': 12, '10': 'value'}, - ], -}; - -/// Descriptor for `Any`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List anyDescriptor = - $convert.base64Decode('CgNBbnkSGQoIdHlwZV91cmwYASABKAlSB3R5cGVVcmwSFAoFdmFsdWUYAiABKAxSBXZhbHVl'); diff --git a/packages/buildbucket-dart/lib/src/generated/google/protobuf/duration.pb.dart b/packages/buildbucket-dart/lib/src/generated/google/protobuf/duration.pb.dart deleted file mode 100644 index 02c1adb70..000000000 --- a/packages/buildbucket-dart/lib/src/generated/google/protobuf/duration.pb.dart +++ /dev/null @@ -1,81 +0,0 @@ -// -// Generated code. Do not modify. -// source: google/protobuf/duration.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:core' as $core; - -import 'package:fixnum/fixnum.dart' as $fixnum; -import 'package:protobuf/protobuf.dart' as $pb; -import 'package:protobuf/src/protobuf/mixins/well_known.dart' as $mixin; - -class Duration extends $pb.GeneratedMessage with $mixin.DurationMixin { - factory Duration() => create(); - Duration._() : super(); - factory Duration.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory Duration.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Duration', - package: const $pb.PackageName(_omitMessageNames ? '' : 'google.protobuf'), - createEmptyInstance: create, - toProto3Json: $mixin.DurationMixin.toProto3JsonHelper, - fromProto3Json: $mixin.DurationMixin.fromProto3JsonHelper) - ..aInt64(1, _omitFieldNames ? '' : 'seconds') - ..a<$core.int>(2, _omitFieldNames ? '' : 'nanos', $pb.PbFieldType.O3) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - Duration clone() => Duration()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - Duration copyWith(void Function(Duration) updates) => - super.copyWith((message) => updates(message as Duration)) as Duration; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static Duration create() => Duration._(); - Duration createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Duration getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Duration? _defaultInstance; - - @$pb.TagNumber(1) - $fixnum.Int64 get seconds => $_getI64(0); - @$pb.TagNumber(1) - set seconds($fixnum.Int64 v) { - $_setInt64(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasSeconds() => $_has(0); - @$pb.TagNumber(1) - void clearSeconds() => clearField(1); - - @$pb.TagNumber(2) - $core.int get nanos => $_getIZ(1); - @$pb.TagNumber(2) - set nanos($core.int v) { - $_setSignedInt32(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasNanos() => $_has(1); - @$pb.TagNumber(2) - void clearNanos() => clearField(2); -} - -const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); -const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/packages/buildbucket-dart/lib/src/generated/google/protobuf/duration.pbenum.dart b/packages/buildbucket-dart/lib/src/generated/google/protobuf/duration.pbenum.dart deleted file mode 100644 index 67dbba200..000000000 --- a/packages/buildbucket-dart/lib/src/generated/google/protobuf/duration.pbenum.dart +++ /dev/null @@ -1,10 +0,0 @@ -// -// Generated code. Do not modify. -// source: google/protobuf/duration.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import diff --git a/packages/buildbucket-dart/lib/src/generated/google/protobuf/duration.pbjson.dart b/packages/buildbucket-dart/lib/src/generated/google/protobuf/duration.pbjson.dart deleted file mode 100644 index b7279f575..000000000 --- a/packages/buildbucket-dart/lib/src/generated/google/protobuf/duration.pbjson.dart +++ /dev/null @@ -1,28 +0,0 @@ -// -// Generated code. Do not modify. -// source: google/protobuf/duration.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:convert' as $convert; -import 'dart:core' as $core; -import 'dart:typed_data' as $typed_data; - -@$core.Deprecated('Use durationDescriptor instead') -const Duration$json = { - '1': 'Duration', - '2': [ - {'1': 'seconds', '3': 1, '4': 1, '5': 3, '10': 'seconds'}, - {'1': 'nanos', '3': 2, '4': 1, '5': 5, '10': 'nanos'}, - ], -}; - -/// Descriptor for `Duration`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List durationDescriptor = - $convert.base64Decode('CghEdXJhdGlvbhIYCgdzZWNvbmRzGAEgASgDUgdzZWNvbmRzEhQKBW5hbm9zGAIgASgFUgVuYW' - '5vcw=='); diff --git a/packages/buildbucket-dart/lib/src/generated/google/protobuf/duration.pbserver.dart b/packages/buildbucket-dart/lib/src/generated/google/protobuf/duration.pbserver.dart deleted file mode 100644 index 5ef4da4e6..000000000 --- a/packages/buildbucket-dart/lib/src/generated/google/protobuf/duration.pbserver.dart +++ /dev/null @@ -1,8 +0,0 @@ -/// -// Generated code. Do not modify. -// source: google/protobuf/duration.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 'duration.pb.dart'; diff --git a/packages/buildbucket-dart/lib/src/generated/google/protobuf/empty.pb.dart b/packages/buildbucket-dart/lib/src/generated/google/protobuf/empty.pb.dart deleted file mode 100644 index 6189f68bc..000000000 --- a/packages/buildbucket-dart/lib/src/generated/google/protobuf/empty.pb.dart +++ /dev/null @@ -1,48 +0,0 @@ -// -// Generated code. Do not modify. -// source: google/protobuf/empty.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:core' as $core; - -import 'package:protobuf/protobuf.dart' as $pb; - -class Empty extends $pb.GeneratedMessage { - factory Empty() => create(); - Empty._() : super(); - factory Empty.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory Empty.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Empty', - package: const $pb.PackageName(_omitMessageNames ? '' : 'google.protobuf'), createEmptyInstance: create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - Empty clone() => Empty()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - Empty copyWith(void Function(Empty) updates) => super.copyWith((message) => updates(message as Empty)) as Empty; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static Empty create() => Empty._(); - Empty createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Empty getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Empty? _defaultInstance; -} - -const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/packages/buildbucket-dart/lib/src/generated/google/protobuf/empty.pbenum.dart b/packages/buildbucket-dart/lib/src/generated/google/protobuf/empty.pbenum.dart deleted file mode 100644 index 443ffb644..000000000 --- a/packages/buildbucket-dart/lib/src/generated/google/protobuf/empty.pbenum.dart +++ /dev/null @@ -1,10 +0,0 @@ -// -// Generated code. Do not modify. -// source: google/protobuf/empty.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import diff --git a/packages/buildbucket-dart/lib/src/generated/google/protobuf/empty.pbjson.dart b/packages/buildbucket-dart/lib/src/generated/google/protobuf/empty.pbjson.dart deleted file mode 100644 index b65750499..000000000 --- a/packages/buildbucket-dart/lib/src/generated/google/protobuf/empty.pbjson.dart +++ /dev/null @@ -1,22 +0,0 @@ -// -// Generated code. Do not modify. -// source: google/protobuf/empty.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:convert' as $convert; -import 'dart:core' as $core; -import 'dart:typed_data' as $typed_data; - -@$core.Deprecated('Use emptyDescriptor instead') -const Empty$json = { - '1': 'Empty', -}; - -/// Descriptor for `Empty`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List emptyDescriptor = $convert.base64Decode('CgVFbXB0eQ=='); diff --git a/packages/buildbucket-dart/lib/src/generated/google/protobuf/field_mask.pb.dart b/packages/buildbucket-dart/lib/src/generated/google/protobuf/field_mask.pb.dart deleted file mode 100644 index 9accd73a5..000000000 --- a/packages/buildbucket-dart/lib/src/generated/google/protobuf/field_mask.pb.dart +++ /dev/null @@ -1,58 +0,0 @@ -// -// Generated code. Do not modify. -// source: google/protobuf/field_mask.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:core' as $core; - -import 'package:protobuf/protobuf.dart' as $pb; -import 'package:protobuf/src/protobuf/mixins/well_known.dart' as $mixin; - -class FieldMask extends $pb.GeneratedMessage with $mixin.FieldMaskMixin { - factory FieldMask() => create(); - FieldMask._() : super(); - factory FieldMask.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory FieldMask.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'FieldMask', - package: const $pb.PackageName(_omitMessageNames ? '' : 'google.protobuf'), - createEmptyInstance: create, - toProto3Json: $mixin.FieldMaskMixin.toProto3JsonHelper, - fromProto3Json: $mixin.FieldMaskMixin.fromProto3JsonHelper) - ..pPS(1, _omitFieldNames ? '' : 'paths') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - FieldMask clone() => FieldMask()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - FieldMask copyWith(void Function(FieldMask) updates) => - super.copyWith((message) => updates(message as FieldMask)) as FieldMask; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static FieldMask create() => FieldMask._(); - FieldMask createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static FieldMask getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static FieldMask? _defaultInstance; - - @$pb.TagNumber(1) - $core.List<$core.String> get paths => $_getList(0); -} - -const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); -const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/packages/buildbucket-dart/lib/src/generated/google/protobuf/field_mask.pbenum.dart b/packages/buildbucket-dart/lib/src/generated/google/protobuf/field_mask.pbenum.dart deleted file mode 100644 index 599996e99..000000000 --- a/packages/buildbucket-dart/lib/src/generated/google/protobuf/field_mask.pbenum.dart +++ /dev/null @@ -1,10 +0,0 @@ -// -// Generated code. Do not modify. -// source: google/protobuf/field_mask.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import diff --git a/packages/buildbucket-dart/lib/src/generated/google/protobuf/field_mask.pbjson.dart b/packages/buildbucket-dart/lib/src/generated/google/protobuf/field_mask.pbjson.dart deleted file mode 100644 index 3f8c538c9..000000000 --- a/packages/buildbucket-dart/lib/src/generated/google/protobuf/field_mask.pbjson.dart +++ /dev/null @@ -1,25 +0,0 @@ -// -// Generated code. Do not modify. -// source: google/protobuf/field_mask.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:convert' as $convert; -import 'dart:core' as $core; -import 'dart:typed_data' as $typed_data; - -@$core.Deprecated('Use fieldMaskDescriptor instead') -const FieldMask$json = { - '1': 'FieldMask', - '2': [ - {'1': 'paths', '3': 1, '4': 3, '5': 9, '10': 'paths'}, - ], -}; - -/// Descriptor for `FieldMask`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List fieldMaskDescriptor = $convert.base64Decode('CglGaWVsZE1hc2sSFAoFcGF0aHMYASADKAlSBXBhdGhz'); diff --git a/packages/buildbucket-dart/lib/src/generated/google/protobuf/struct.pb.dart b/packages/buildbucket-dart/lib/src/generated/google/protobuf/struct.pb.dart deleted file mode 100644 index 900db435f..000000000 --- a/packages/buildbucket-dart/lib/src/generated/google/protobuf/struct.pb.dart +++ /dev/null @@ -1,240 +0,0 @@ -// -// Generated code. Do not modify. -// source: google/protobuf/struct.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:core' as $core; - -import 'package:protobuf/protobuf.dart' as $pb; -import 'package:protobuf/src/protobuf/mixins/well_known.dart' as $mixin; - -import 'struct.pbenum.dart'; - -export 'struct.pbenum.dart'; - -class Struct extends $pb.GeneratedMessage with $mixin.StructMixin { - factory Struct() => create(); - Struct._() : super(); - factory Struct.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory Struct.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Struct', - package: const $pb.PackageName(_omitMessageNames ? '' : 'google.protobuf'), - createEmptyInstance: create, - toProto3Json: $mixin.StructMixin.toProto3JsonHelper, - fromProto3Json: $mixin.StructMixin.fromProto3JsonHelper) - ..m<$core.String, Value>(1, _omitFieldNames ? '' : 'fields', - entryClassName: 'Struct.FieldsEntry', - keyFieldType: $pb.PbFieldType.OS, - valueFieldType: $pb.PbFieldType.OM, - valueCreator: Value.create, - valueDefaultOrMaker: Value.getDefault, - packageName: const $pb.PackageName('google.protobuf')) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - Struct clone() => Struct()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - Struct copyWith(void Function(Struct) updates) => super.copyWith((message) => updates(message as Struct)) as Struct; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static Struct create() => Struct._(); - Struct createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Struct getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Struct? _defaultInstance; - - @$pb.TagNumber(1) - $core.Map<$core.String, Value> get fields => $_getMap(0); -} - -enum Value_Kind { nullValue, numberValue, stringValue, boolValue, structValue, listValue, notSet } - -class Value extends $pb.GeneratedMessage with $mixin.ValueMixin { - factory Value() => create(); - Value._() : super(); - factory Value.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory Value.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static const $core.Map<$core.int, Value_Kind> _Value_KindByTag = { - 1: Value_Kind.nullValue, - 2: Value_Kind.numberValue, - 3: Value_Kind.stringValue, - 4: Value_Kind.boolValue, - 5: Value_Kind.structValue, - 6: Value_Kind.listValue, - 0: Value_Kind.notSet - }; - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Value', - package: const $pb.PackageName(_omitMessageNames ? '' : 'google.protobuf'), - createEmptyInstance: create, - toProto3Json: $mixin.ValueMixin.toProto3JsonHelper, - fromProto3Json: $mixin.ValueMixin.fromProto3JsonHelper) - ..oo(0, [1, 2, 3, 4, 5, 6]) - ..e(1, _omitFieldNames ? '' : 'nullValue', $pb.PbFieldType.OE, - defaultOrMaker: NullValue.NULL_VALUE, valueOf: NullValue.valueOf, enumValues: NullValue.values) - ..a<$core.double>(2, _omitFieldNames ? '' : 'numberValue', $pb.PbFieldType.OD) - ..aOS(3, _omitFieldNames ? '' : 'stringValue') - ..aOB(4, _omitFieldNames ? '' : 'boolValue') - ..aOM(5, _omitFieldNames ? '' : 'structValue', subBuilder: Struct.create) - ..aOM(6, _omitFieldNames ? '' : 'listValue', subBuilder: ListValue.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - Value clone() => Value()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - Value copyWith(void Function(Value) updates) => super.copyWith((message) => updates(message as Value)) as Value; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static Value create() => Value._(); - Value createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Value getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Value? _defaultInstance; - - Value_Kind whichKind() => _Value_KindByTag[$_whichOneof(0)]!; - void clearKind() => clearField($_whichOneof(0)); - - @$pb.TagNumber(1) - NullValue get nullValue => $_getN(0); - @$pb.TagNumber(1) - set nullValue(NullValue v) { - setField(1, v); - } - - @$pb.TagNumber(1) - $core.bool hasNullValue() => $_has(0); - @$pb.TagNumber(1) - void clearNullValue() => clearField(1); - - @$pb.TagNumber(2) - $core.double get numberValue => $_getN(1); - @$pb.TagNumber(2) - set numberValue($core.double v) { - $_setDouble(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasNumberValue() => $_has(1); - @$pb.TagNumber(2) - void clearNumberValue() => clearField(2); - - @$pb.TagNumber(3) - $core.String get stringValue => $_getSZ(2); - @$pb.TagNumber(3) - set stringValue($core.String v) { - $_setString(2, v); - } - - @$pb.TagNumber(3) - $core.bool hasStringValue() => $_has(2); - @$pb.TagNumber(3) - void clearStringValue() => clearField(3); - - @$pb.TagNumber(4) - $core.bool get boolValue => $_getBF(3); - @$pb.TagNumber(4) - set boolValue($core.bool v) { - $_setBool(3, v); - } - - @$pb.TagNumber(4) - $core.bool hasBoolValue() => $_has(3); - @$pb.TagNumber(4) - void clearBoolValue() => clearField(4); - - @$pb.TagNumber(5) - Struct get structValue => $_getN(4); - @$pb.TagNumber(5) - set structValue(Struct v) { - setField(5, v); - } - - @$pb.TagNumber(5) - $core.bool hasStructValue() => $_has(4); - @$pb.TagNumber(5) - void clearStructValue() => clearField(5); - @$pb.TagNumber(5) - Struct ensureStructValue() => $_ensure(4); - - @$pb.TagNumber(6) - ListValue get listValue => $_getN(5); - @$pb.TagNumber(6) - set listValue(ListValue v) { - setField(6, v); - } - - @$pb.TagNumber(6) - $core.bool hasListValue() => $_has(5); - @$pb.TagNumber(6) - void clearListValue() => clearField(6); - @$pb.TagNumber(6) - ListValue ensureListValue() => $_ensure(5); -} - -class ListValue extends $pb.GeneratedMessage with $mixin.ListValueMixin { - factory ListValue() => create(); - ListValue._() : super(); - factory ListValue.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory ListValue.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ListValue', - package: const $pb.PackageName(_omitMessageNames ? '' : 'google.protobuf'), - createEmptyInstance: create, - toProto3Json: $mixin.ListValueMixin.toProto3JsonHelper, - fromProto3Json: $mixin.ListValueMixin.fromProto3JsonHelper) - ..pc(1, _omitFieldNames ? '' : 'values', $pb.PbFieldType.PM, subBuilder: Value.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - ListValue clone() => ListValue()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - ListValue copyWith(void Function(ListValue) updates) => - super.copyWith((message) => updates(message as ListValue)) as ListValue; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static ListValue create() => ListValue._(); - ListValue createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static ListValue getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static ListValue? _defaultInstance; - - @$pb.TagNumber(1) - $core.List get values => $_getList(0); -} - -const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); -const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/packages/buildbucket-dart/lib/src/generated/google/protobuf/struct.pbenum.dart b/packages/buildbucket-dart/lib/src/generated/google/protobuf/struct.pbenum.dart deleted file mode 100644 index 61f7a1581..000000000 --- a/packages/buildbucket-dart/lib/src/generated/google/protobuf/struct.pbenum.dart +++ /dev/null @@ -1,29 +0,0 @@ -// -// Generated code. Do not modify. -// source: google/protobuf/struct.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:core' as $core; - -import 'package:protobuf/protobuf.dart' as $pb; - -class NullValue extends $pb.ProtobufEnum { - static const NullValue NULL_VALUE = NullValue._(0, _omitEnumNames ? '' : 'NULL_VALUE'); - - static const $core.List values = [ - NULL_VALUE, - ]; - - static final $core.Map<$core.int, NullValue> _byValue = $pb.ProtobufEnum.initByValue(values); - static NullValue? valueOf($core.int value) => _byValue[value]; - - const NullValue._($core.int v, $core.String n) : super(v, n); -} - -const _omitEnumNames = $core.bool.fromEnvironment('protobuf.omit_enum_names'); diff --git a/packages/buildbucket-dart/lib/src/generated/google/protobuf/struct.pbjson.dart b/packages/buildbucket-dart/lib/src/generated/google/protobuf/struct.pbjson.dart deleted file mode 100644 index e0bc8c9ce..000000000 --- a/packages/buildbucket-dart/lib/src/generated/google/protobuf/struct.pbjson.dart +++ /dev/null @@ -1,88 +0,0 @@ -// -// Generated code. Do not modify. -// source: google/protobuf/struct.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:convert' as $convert; -import 'dart:core' as $core; -import 'dart:typed_data' as $typed_data; - -@$core.Deprecated('Use nullValueDescriptor instead') -const NullValue$json = { - '1': 'NullValue', - '2': [ - {'1': 'NULL_VALUE', '2': 0}, - ], -}; - -/// Descriptor for `NullValue`. Decode as a `google.protobuf.EnumDescriptorProto`. -final $typed_data.Uint8List nullValueDescriptor = $convert.base64Decode('CglOdWxsVmFsdWUSDgoKTlVMTF9WQUxVRRAA'); - -@$core.Deprecated('Use structDescriptor instead') -const Struct$json = { - '1': 'Struct', - '2': [ - {'1': 'fields', '3': 1, '4': 3, '5': 11, '6': '.google.protobuf.Struct.FieldsEntry', '10': 'fields'}, - ], - '3': [Struct_FieldsEntry$json], -}; - -@$core.Deprecated('Use structDescriptor instead') -const Struct_FieldsEntry$json = { - '1': 'FieldsEntry', - '2': [ - {'1': 'key', '3': 1, '4': 1, '5': 9, '10': 'key'}, - {'1': 'value', '3': 2, '4': 1, '5': 11, '6': '.google.protobuf.Value', '10': 'value'}, - ], - '7': {'7': true}, -}; - -/// Descriptor for `Struct`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List structDescriptor = - $convert.base64Decode('CgZTdHJ1Y3QSOwoGZmllbGRzGAEgAygLMiMuZ29vZ2xlLnByb3RvYnVmLlN0cnVjdC5GaWVsZH' - 'NFbnRyeVIGZmllbGRzGlEKC0ZpZWxkc0VudHJ5EhAKA2tleRgBIAEoCVIDa2V5EiwKBXZhbHVl' - 'GAIgASgLMhYuZ29vZ2xlLnByb3RvYnVmLlZhbHVlUgV2YWx1ZToCOAE='); - -@$core.Deprecated('Use valueDescriptor instead') -const Value$json = { - '1': 'Value', - '2': [ - {'1': 'null_value', '3': 1, '4': 1, '5': 14, '6': '.google.protobuf.NullValue', '9': 0, '10': 'nullValue'}, - {'1': 'number_value', '3': 2, '4': 1, '5': 1, '9': 0, '10': 'numberValue'}, - {'1': 'string_value', '3': 3, '4': 1, '5': 9, '9': 0, '10': 'stringValue'}, - {'1': 'bool_value', '3': 4, '4': 1, '5': 8, '9': 0, '10': 'boolValue'}, - {'1': 'struct_value', '3': 5, '4': 1, '5': 11, '6': '.google.protobuf.Struct', '9': 0, '10': 'structValue'}, - {'1': 'list_value', '3': 6, '4': 1, '5': 11, '6': '.google.protobuf.ListValue', '9': 0, '10': 'listValue'}, - ], - '8': [ - {'1': 'kind'}, - ], -}; - -/// Descriptor for `Value`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List valueDescriptor = - $convert.base64Decode('CgVWYWx1ZRI7CgpudWxsX3ZhbHVlGAEgASgOMhouZ29vZ2xlLnByb3RvYnVmLk51bGxWYWx1ZU' - 'gAUgludWxsVmFsdWUSIwoMbnVtYmVyX3ZhbHVlGAIgASgBSABSC251bWJlclZhbHVlEiMKDHN0' - 'cmluZ192YWx1ZRgDIAEoCUgAUgtzdHJpbmdWYWx1ZRIfCgpib29sX3ZhbHVlGAQgASgISABSCW' - 'Jvb2xWYWx1ZRI8CgxzdHJ1Y3RfdmFsdWUYBSABKAsyFy5nb29nbGUucHJvdG9idWYuU3RydWN0' - 'SABSC3N0cnVjdFZhbHVlEjsKCmxpc3RfdmFsdWUYBiABKAsyGi5nb29nbGUucHJvdG9idWYuTG' - 'lzdFZhbHVlSABSCWxpc3RWYWx1ZUIGCgRraW5k'); - -@$core.Deprecated('Use listValueDescriptor instead') -const ListValue$json = { - '1': 'ListValue', - '2': [ - {'1': 'values', '3': 1, '4': 3, '5': 11, '6': '.google.protobuf.Value', '10': 'values'}, - ], -}; - -/// Descriptor for `ListValue`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List listValueDescriptor = - $convert.base64Decode('CglMaXN0VmFsdWUSLgoGdmFsdWVzGAEgAygLMhYuZ29vZ2xlLnByb3RvYnVmLlZhbHVlUgZ2YW' - 'x1ZXM='); diff --git a/packages/buildbucket-dart/lib/src/generated/google/protobuf/struct.pbserver.dart b/packages/buildbucket-dart/lib/src/generated/google/protobuf/struct.pbserver.dart deleted file mode 100644 index d1617f88d..000000000 --- a/packages/buildbucket-dart/lib/src/generated/google/protobuf/struct.pbserver.dart +++ /dev/null @@ -1,8 +0,0 @@ -/// -// Generated code. Do not modify. -// source: google/protobuf/struct.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 'struct.pb.dart'; diff --git a/packages/buildbucket-dart/lib/src/generated/google/protobuf/timestamp.pb.dart b/packages/buildbucket-dart/lib/src/generated/google/protobuf/timestamp.pb.dart deleted file mode 100644 index 1e4fbb1cb..000000000 --- a/packages/buildbucket-dart/lib/src/generated/google/protobuf/timestamp.pb.dart +++ /dev/null @@ -1,90 +0,0 @@ -// -// Generated code. Do not modify. -// source: google/protobuf/timestamp.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:core' as $core; - -import 'package:fixnum/fixnum.dart' as $fixnum; -import 'package:protobuf/protobuf.dart' as $pb; -import 'package:protobuf/src/protobuf/mixins/well_known.dart' as $mixin; - -class Timestamp extends $pb.GeneratedMessage with $mixin.TimestampMixin { - factory Timestamp() => create(); - Timestamp._() : super(); - factory Timestamp.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory Timestamp.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Timestamp', - package: const $pb.PackageName(_omitMessageNames ? '' : 'google.protobuf'), - createEmptyInstance: create, - toProto3Json: $mixin.TimestampMixin.toProto3JsonHelper, - fromProto3Json: $mixin.TimestampMixin.fromProto3JsonHelper) - ..aInt64(1, _omitFieldNames ? '' : 'seconds') - ..a<$core.int>(2, _omitFieldNames ? '' : 'nanos', $pb.PbFieldType.O3) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - Timestamp clone() => Timestamp()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - Timestamp copyWith(void Function(Timestamp) updates) => - super.copyWith((message) => updates(message as Timestamp)) as Timestamp; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static Timestamp create() => Timestamp._(); - Timestamp createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Timestamp getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Timestamp? _defaultInstance; - - @$pb.TagNumber(1) - $fixnum.Int64 get seconds => $_getI64(0); - @$pb.TagNumber(1) - set seconds($fixnum.Int64 v) { - $_setInt64(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasSeconds() => $_has(0); - @$pb.TagNumber(1) - void clearSeconds() => clearField(1); - - @$pb.TagNumber(2) - $core.int get nanos => $_getIZ(1); - @$pb.TagNumber(2) - set nanos($core.int v) { - $_setSignedInt32(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasNanos() => $_has(1); - @$pb.TagNumber(2) - void clearNanos() => clearField(2); - - /// Creates a new instance from [dateTime]. - /// - /// Time zone information will not be preserved. - static Timestamp fromDateTime($core.DateTime dateTime) { - final result = create(); - $mixin.TimestampMixin.setFromDateTime(result, dateTime); - return result; - } -} - -const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); -const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/packages/buildbucket-dart/lib/src/generated/google/protobuf/timestamp.pbenum.dart b/packages/buildbucket-dart/lib/src/generated/google/protobuf/timestamp.pbenum.dart deleted file mode 100644 index 2f5d300ee..000000000 --- a/packages/buildbucket-dart/lib/src/generated/google/protobuf/timestamp.pbenum.dart +++ /dev/null @@ -1,10 +0,0 @@ -// -// Generated code. Do not modify. -// source: google/protobuf/timestamp.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import diff --git a/packages/buildbucket-dart/lib/src/generated/google/protobuf/timestamp.pbjson.dart b/packages/buildbucket-dart/lib/src/generated/google/protobuf/timestamp.pbjson.dart deleted file mode 100644 index 14172e75d..000000000 --- a/packages/buildbucket-dart/lib/src/generated/google/protobuf/timestamp.pbjson.dart +++ /dev/null @@ -1,28 +0,0 @@ -// -// Generated code. Do not modify. -// source: google/protobuf/timestamp.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:convert' as $convert; -import 'dart:core' as $core; -import 'dart:typed_data' as $typed_data; - -@$core.Deprecated('Use timestampDescriptor instead') -const Timestamp$json = { - '1': 'Timestamp', - '2': [ - {'1': 'seconds', '3': 1, '4': 1, '5': 3, '10': 'seconds'}, - {'1': 'nanos', '3': 2, '4': 1, '5': 5, '10': 'nanos'}, - ], -}; - -/// Descriptor for `Timestamp`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List timestampDescriptor = - $convert.base64Decode('CglUaW1lc3RhbXASGAoHc2Vjb25kcxgBIAEoA1IHc2Vjb25kcxIUCgVuYW5vcxgCIAEoBVIFbm' - 'Fub3M='); diff --git a/packages/buildbucket-dart/lib/src/generated/google/protobuf/timestamp.pbserver.dart b/packages/buildbucket-dart/lib/src/generated/google/protobuf/timestamp.pbserver.dart deleted file mode 100644 index 991020682..000000000 --- a/packages/buildbucket-dart/lib/src/generated/google/protobuf/timestamp.pbserver.dart +++ /dev/null @@ -1,8 +0,0 @@ -/// -// Generated code. Do not modify. -// source: google/protobuf/timestamp.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 'timestamp.pb.dart'; diff --git a/packages/buildbucket-dart/lib/src/generated/google/protobuf/wrappers.pb.dart b/packages/buildbucket-dart/lib/src/generated/google/protobuf/wrappers.pb.dart deleted file mode 100644 index 6799b1f0a..000000000 --- a/packages/buildbucket-dart/lib/src/generated/google/protobuf/wrappers.pb.dart +++ /dev/null @@ -1,460 +0,0 @@ -// -// Generated code. Do not modify. -// source: google/protobuf/wrappers.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:core' as $core; - -import 'package:fixnum/fixnum.dart' as $fixnum; -import 'package:protobuf/protobuf.dart' as $pb; -import 'package:protobuf/src/protobuf/mixins/well_known.dart' as $mixin; - -class DoubleValue extends $pb.GeneratedMessage with $mixin.DoubleValueMixin { - factory DoubleValue() => create(); - DoubleValue._() : super(); - factory DoubleValue.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory DoubleValue.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'DoubleValue', - package: const $pb.PackageName(_omitMessageNames ? '' : 'google.protobuf'), - createEmptyInstance: create, - toProto3Json: $mixin.DoubleValueMixin.toProto3JsonHelper, - fromProto3Json: $mixin.DoubleValueMixin.fromProto3JsonHelper) - ..a<$core.double>(1, _omitFieldNames ? '' : 'value', $pb.PbFieldType.OD) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - DoubleValue clone() => DoubleValue()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - DoubleValue copyWith(void Function(DoubleValue) updates) => - super.copyWith((message) => updates(message as DoubleValue)) as DoubleValue; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static DoubleValue create() => DoubleValue._(); - DoubleValue createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static DoubleValue getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static DoubleValue? _defaultInstance; - - @$pb.TagNumber(1) - $core.double get value => $_getN(0); - @$pb.TagNumber(1) - set value($core.double v) { - $_setDouble(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasValue() => $_has(0); - @$pb.TagNumber(1) - void clearValue() => clearField(1); -} - -class FloatValue extends $pb.GeneratedMessage with $mixin.FloatValueMixin { - factory FloatValue() => create(); - FloatValue._() : super(); - factory FloatValue.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory FloatValue.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'FloatValue', - package: const $pb.PackageName(_omitMessageNames ? '' : 'google.protobuf'), - createEmptyInstance: create, - toProto3Json: $mixin.FloatValueMixin.toProto3JsonHelper, - fromProto3Json: $mixin.FloatValueMixin.fromProto3JsonHelper) - ..a<$core.double>(1, _omitFieldNames ? '' : 'value', $pb.PbFieldType.OF) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - FloatValue clone() => FloatValue()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - FloatValue copyWith(void Function(FloatValue) updates) => - super.copyWith((message) => updates(message as FloatValue)) as FloatValue; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static FloatValue create() => FloatValue._(); - FloatValue createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static FloatValue getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static FloatValue? _defaultInstance; - - @$pb.TagNumber(1) - $core.double get value => $_getN(0); - @$pb.TagNumber(1) - set value($core.double v) { - $_setFloat(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasValue() => $_has(0); - @$pb.TagNumber(1) - void clearValue() => clearField(1); -} - -class Int64Value extends $pb.GeneratedMessage with $mixin.Int64ValueMixin { - factory Int64Value() => create(); - Int64Value._() : super(); - factory Int64Value.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory Int64Value.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Int64Value', - package: const $pb.PackageName(_omitMessageNames ? '' : 'google.protobuf'), - createEmptyInstance: create, - toProto3Json: $mixin.Int64ValueMixin.toProto3JsonHelper, - fromProto3Json: $mixin.Int64ValueMixin.fromProto3JsonHelper) - ..aInt64(1, _omitFieldNames ? '' : 'value') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - Int64Value clone() => Int64Value()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - Int64Value copyWith(void Function(Int64Value) updates) => - super.copyWith((message) => updates(message as Int64Value)) as Int64Value; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static Int64Value create() => Int64Value._(); - Int64Value createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Int64Value getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Int64Value? _defaultInstance; - - @$pb.TagNumber(1) - $fixnum.Int64 get value => $_getI64(0); - @$pb.TagNumber(1) - set value($fixnum.Int64 v) { - $_setInt64(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasValue() => $_has(0); - @$pb.TagNumber(1) - void clearValue() => clearField(1); -} - -class UInt64Value extends $pb.GeneratedMessage with $mixin.UInt64ValueMixin { - factory UInt64Value() => create(); - UInt64Value._() : super(); - factory UInt64Value.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory UInt64Value.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'UInt64Value', - package: const $pb.PackageName(_omitMessageNames ? '' : 'google.protobuf'), - createEmptyInstance: create, - toProto3Json: $mixin.UInt64ValueMixin.toProto3JsonHelper, - fromProto3Json: $mixin.UInt64ValueMixin.fromProto3JsonHelper) - ..a<$fixnum.Int64>(1, _omitFieldNames ? '' : 'value', $pb.PbFieldType.OU6, defaultOrMaker: $fixnum.Int64.ZERO) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - UInt64Value clone() => UInt64Value()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - UInt64Value copyWith(void Function(UInt64Value) updates) => - super.copyWith((message) => updates(message as UInt64Value)) as UInt64Value; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static UInt64Value create() => UInt64Value._(); - UInt64Value createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static UInt64Value getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static UInt64Value? _defaultInstance; - - @$pb.TagNumber(1) - $fixnum.Int64 get value => $_getI64(0); - @$pb.TagNumber(1) - set value($fixnum.Int64 v) { - $_setInt64(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasValue() => $_has(0); - @$pb.TagNumber(1) - void clearValue() => clearField(1); -} - -class Int32Value extends $pb.GeneratedMessage with $mixin.Int32ValueMixin { - factory Int32Value() => create(); - Int32Value._() : super(); - factory Int32Value.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory Int32Value.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Int32Value', - package: const $pb.PackageName(_omitMessageNames ? '' : 'google.protobuf'), - createEmptyInstance: create, - toProto3Json: $mixin.Int32ValueMixin.toProto3JsonHelper, - fromProto3Json: $mixin.Int32ValueMixin.fromProto3JsonHelper) - ..a<$core.int>(1, _omitFieldNames ? '' : 'value', $pb.PbFieldType.O3) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - Int32Value clone() => Int32Value()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - Int32Value copyWith(void Function(Int32Value) updates) => - super.copyWith((message) => updates(message as Int32Value)) as Int32Value; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static Int32Value create() => Int32Value._(); - Int32Value createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Int32Value getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Int32Value? _defaultInstance; - - @$pb.TagNumber(1) - $core.int get value => $_getIZ(0); - @$pb.TagNumber(1) - set value($core.int v) { - $_setSignedInt32(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasValue() => $_has(0); - @$pb.TagNumber(1) - void clearValue() => clearField(1); -} - -class UInt32Value extends $pb.GeneratedMessage with $mixin.UInt32ValueMixin { - factory UInt32Value() => create(); - UInt32Value._() : super(); - factory UInt32Value.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory UInt32Value.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'UInt32Value', - package: const $pb.PackageName(_omitMessageNames ? '' : 'google.protobuf'), - createEmptyInstance: create, - toProto3Json: $mixin.UInt32ValueMixin.toProto3JsonHelper, - fromProto3Json: $mixin.UInt32ValueMixin.fromProto3JsonHelper) - ..a<$core.int>(1, _omitFieldNames ? '' : 'value', $pb.PbFieldType.OU3) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - UInt32Value clone() => UInt32Value()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - UInt32Value copyWith(void Function(UInt32Value) updates) => - super.copyWith((message) => updates(message as UInt32Value)) as UInt32Value; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static UInt32Value create() => UInt32Value._(); - UInt32Value createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static UInt32Value getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static UInt32Value? _defaultInstance; - - @$pb.TagNumber(1) - $core.int get value => $_getIZ(0); - @$pb.TagNumber(1) - set value($core.int v) { - $_setUnsignedInt32(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasValue() => $_has(0); - @$pb.TagNumber(1) - void clearValue() => clearField(1); -} - -class BoolValue extends $pb.GeneratedMessage with $mixin.BoolValueMixin { - factory BoolValue() => create(); - BoolValue._() : super(); - factory BoolValue.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BoolValue.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BoolValue', - package: const $pb.PackageName(_omitMessageNames ? '' : 'google.protobuf'), - createEmptyInstance: create, - toProto3Json: $mixin.BoolValueMixin.toProto3JsonHelper, - fromProto3Json: $mixin.BoolValueMixin.fromProto3JsonHelper) - ..aOB(1, _omitFieldNames ? '' : 'value') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BoolValue clone() => BoolValue()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BoolValue copyWith(void Function(BoolValue) updates) => - super.copyWith((message) => updates(message as BoolValue)) as BoolValue; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BoolValue create() => BoolValue._(); - BoolValue createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BoolValue getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BoolValue? _defaultInstance; - - @$pb.TagNumber(1) - $core.bool get value => $_getBF(0); - @$pb.TagNumber(1) - set value($core.bool v) { - $_setBool(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasValue() => $_has(0); - @$pb.TagNumber(1) - void clearValue() => clearField(1); -} - -class StringValue extends $pb.GeneratedMessage with $mixin.StringValueMixin { - factory StringValue() => create(); - StringValue._() : super(); - factory StringValue.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory StringValue.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'StringValue', - package: const $pb.PackageName(_omitMessageNames ? '' : 'google.protobuf'), - createEmptyInstance: create, - toProto3Json: $mixin.StringValueMixin.toProto3JsonHelper, - fromProto3Json: $mixin.StringValueMixin.fromProto3JsonHelper) - ..aOS(1, _omitFieldNames ? '' : 'value') - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - StringValue clone() => StringValue()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - StringValue copyWith(void Function(StringValue) updates) => - super.copyWith((message) => updates(message as StringValue)) as StringValue; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static StringValue create() => StringValue._(); - StringValue createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static StringValue getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static StringValue? _defaultInstance; - - @$pb.TagNumber(1) - $core.String get value => $_getSZ(0); - @$pb.TagNumber(1) - set value($core.String v) { - $_setString(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasValue() => $_has(0); - @$pb.TagNumber(1) - void clearValue() => clearField(1); -} - -class BytesValue extends $pb.GeneratedMessage with $mixin.BytesValueMixin { - factory BytesValue() => create(); - BytesValue._() : super(); - factory BytesValue.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory BytesValue.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'BytesValue', - package: const $pb.PackageName(_omitMessageNames ? '' : 'google.protobuf'), - createEmptyInstance: create, - toProto3Json: $mixin.BytesValueMixin.toProto3JsonHelper, - fromProto3Json: $mixin.BytesValueMixin.fromProto3JsonHelper) - ..a<$core.List<$core.int>>(1, _omitFieldNames ? '' : 'value', $pb.PbFieldType.OY) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - BytesValue clone() => BytesValue()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - BytesValue copyWith(void Function(BytesValue) updates) => - super.copyWith((message) => updates(message as BytesValue)) as BytesValue; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static BytesValue create() => BytesValue._(); - BytesValue createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static BytesValue getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static BytesValue? _defaultInstance; - - @$pb.TagNumber(1) - $core.List<$core.int> get value => $_getN(0); - @$pb.TagNumber(1) - set value($core.List<$core.int> v) { - $_setBytes(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasValue() => $_has(0); - @$pb.TagNumber(1) - void clearValue() => clearField(1); -} - -const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); -const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/packages/buildbucket-dart/lib/src/generated/google/protobuf/wrappers.pbenum.dart b/packages/buildbucket-dart/lib/src/generated/google/protobuf/wrappers.pbenum.dart deleted file mode 100644 index c6d8da46a..000000000 --- a/packages/buildbucket-dart/lib/src/generated/google/protobuf/wrappers.pbenum.dart +++ /dev/null @@ -1,10 +0,0 @@ -// -// Generated code. Do not modify. -// source: google/protobuf/wrappers.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import diff --git a/packages/buildbucket-dart/lib/src/generated/google/protobuf/wrappers.pbjson.dart b/packages/buildbucket-dart/lib/src/generated/google/protobuf/wrappers.pbjson.dart deleted file mode 100644 index 6d44cc64b..000000000 --- a/packages/buildbucket-dart/lib/src/generated/google/protobuf/wrappers.pbjson.dart +++ /dev/null @@ -1,121 +0,0 @@ -// -// Generated code. Do not modify. -// source: google/protobuf/wrappers.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:convert' as $convert; -import 'dart:core' as $core; -import 'dart:typed_data' as $typed_data; - -@$core.Deprecated('Use doubleValueDescriptor instead') -const DoubleValue$json = { - '1': 'DoubleValue', - '2': [ - {'1': 'value', '3': 1, '4': 1, '5': 1, '10': 'value'}, - ], -}; - -/// Descriptor for `DoubleValue`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List doubleValueDescriptor = - $convert.base64Decode('CgtEb3VibGVWYWx1ZRIUCgV2YWx1ZRgBIAEoAVIFdmFsdWU='); - -@$core.Deprecated('Use floatValueDescriptor instead') -const FloatValue$json = { - '1': 'FloatValue', - '2': [ - {'1': 'value', '3': 1, '4': 1, '5': 2, '10': 'value'}, - ], -}; - -/// Descriptor for `FloatValue`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List floatValueDescriptor = - $convert.base64Decode('CgpGbG9hdFZhbHVlEhQKBXZhbHVlGAEgASgCUgV2YWx1ZQ=='); - -@$core.Deprecated('Use int64ValueDescriptor instead') -const Int64Value$json = { - '1': 'Int64Value', - '2': [ - {'1': 'value', '3': 1, '4': 1, '5': 3, '10': 'value'}, - ], -}; - -/// Descriptor for `Int64Value`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List int64ValueDescriptor = - $convert.base64Decode('CgpJbnQ2NFZhbHVlEhQKBXZhbHVlGAEgASgDUgV2YWx1ZQ=='); - -@$core.Deprecated('Use uInt64ValueDescriptor instead') -const UInt64Value$json = { - '1': 'UInt64Value', - '2': [ - {'1': 'value', '3': 1, '4': 1, '5': 4, '10': 'value'}, - ], -}; - -/// Descriptor for `UInt64Value`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List uInt64ValueDescriptor = - $convert.base64Decode('CgtVSW50NjRWYWx1ZRIUCgV2YWx1ZRgBIAEoBFIFdmFsdWU='); - -@$core.Deprecated('Use int32ValueDescriptor instead') -const Int32Value$json = { - '1': 'Int32Value', - '2': [ - {'1': 'value', '3': 1, '4': 1, '5': 5, '10': 'value'}, - ], -}; - -/// Descriptor for `Int32Value`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List int32ValueDescriptor = - $convert.base64Decode('CgpJbnQzMlZhbHVlEhQKBXZhbHVlGAEgASgFUgV2YWx1ZQ=='); - -@$core.Deprecated('Use uInt32ValueDescriptor instead') -const UInt32Value$json = { - '1': 'UInt32Value', - '2': [ - {'1': 'value', '3': 1, '4': 1, '5': 13, '10': 'value'}, - ], -}; - -/// Descriptor for `UInt32Value`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List uInt32ValueDescriptor = - $convert.base64Decode('CgtVSW50MzJWYWx1ZRIUCgV2YWx1ZRgBIAEoDVIFdmFsdWU='); - -@$core.Deprecated('Use boolValueDescriptor instead') -const BoolValue$json = { - '1': 'BoolValue', - '2': [ - {'1': 'value', '3': 1, '4': 1, '5': 8, '10': 'value'}, - ], -}; - -/// Descriptor for `BoolValue`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List boolValueDescriptor = $convert.base64Decode('CglCb29sVmFsdWUSFAoFdmFsdWUYASABKAhSBXZhbHVl'); - -@$core.Deprecated('Use stringValueDescriptor instead') -const StringValue$json = { - '1': 'StringValue', - '2': [ - {'1': 'value', '3': 1, '4': 1, '5': 9, '10': 'value'}, - ], -}; - -/// Descriptor for `StringValue`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List stringValueDescriptor = - $convert.base64Decode('CgtTdHJpbmdWYWx1ZRIUCgV2YWx1ZRgBIAEoCVIFdmFsdWU='); - -@$core.Deprecated('Use bytesValueDescriptor instead') -const BytesValue$json = { - '1': 'BytesValue', - '2': [ - {'1': 'value', '3': 1, '4': 1, '5': 12, '10': 'value'}, - ], -}; - -/// Descriptor for `BytesValue`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List bytesValueDescriptor = - $convert.base64Decode('CgpCeXRlc1ZhbHVlEhQKBXZhbHVlGAEgASgMUgV2YWx1ZQ=='); diff --git a/packages/buildbucket-dart/lib/src/generated/google/protobuf/wrappers.pbserver.dart b/packages/buildbucket-dart/lib/src/generated/google/protobuf/wrappers.pbserver.dart deleted file mode 100644 index 359f5824e..000000000 --- a/packages/buildbucket-dart/lib/src/generated/google/protobuf/wrappers.pbserver.dart +++ /dev/null @@ -1,8 +0,0 @@ -/// -// Generated code. Do not modify. -// source: google/protobuf/wrappers.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 'wrappers.pb.dart'; diff --git a/packages/buildbucket-dart/lib/src/generated/google/rpc/status.pb.dart b/packages/buildbucket-dart/lib/src/generated/google/rpc/status.pb.dart deleted file mode 100644 index 34a824802..000000000 --- a/packages/buildbucket-dart/lib/src/generated/google/rpc/status.pb.dart +++ /dev/null @@ -1,81 +0,0 @@ -// -// Generated code. Do not modify. -// source: google/rpc/status.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:core' as $core; - -import 'package:protobuf/protobuf.dart' as $pb; - -import '../protobuf/any.pb.dart' as $0; - -class Status extends $pb.GeneratedMessage { - factory Status() => create(); - Status._() : super(); - factory Status.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromBuffer(i, r); - factory Status.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => - create()..mergeFromJson(i, r); - - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Status', - package: const $pb.PackageName(_omitMessageNames ? '' : 'google.rpc'), createEmptyInstance: create) - ..a<$core.int>(1, _omitFieldNames ? '' : 'code', $pb.PbFieldType.O3) - ..aOS(2, _omitFieldNames ? '' : 'message') - ..pc<$0.Any>(3, _omitFieldNames ? '' : 'details', $pb.PbFieldType.PM, subBuilder: $0.Any.create) - ..hasRequiredFields = false; - - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' - 'Will be removed in next major version') - Status clone() => Status()..mergeFromMessage(this); - @$core.Deprecated('Using this can add significant overhead to your binary. ' - 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' - 'Will be removed in next major version') - Status copyWith(void Function(Status) updates) => super.copyWith((message) => updates(message as Status)) as Status; - - $pb.BuilderInfo get info_ => _i; - - @$core.pragma('dart2js:noInline') - static Status create() => Status._(); - Status createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); - @$core.pragma('dart2js:noInline') - static Status getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static Status? _defaultInstance; - - @$pb.TagNumber(1) - $core.int get code => $_getIZ(0); - @$pb.TagNumber(1) - set code($core.int v) { - $_setSignedInt32(0, v); - } - - @$pb.TagNumber(1) - $core.bool hasCode() => $_has(0); - @$pb.TagNumber(1) - void clearCode() => clearField(1); - - @$pb.TagNumber(2) - $core.String get message => $_getSZ(1); - @$pb.TagNumber(2) - set message($core.String v) { - $_setString(1, v); - } - - @$pb.TagNumber(2) - $core.bool hasMessage() => $_has(1); - @$pb.TagNumber(2) - void clearMessage() => clearField(2); - - @$pb.TagNumber(3) - $core.List<$0.Any> get details => $_getList(2); -} - -const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); -const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/packages/buildbucket-dart/lib/src/generated/google/rpc/status.pbenum.dart b/packages/buildbucket-dart/lib/src/generated/google/rpc/status.pbenum.dart deleted file mode 100644 index f9dfed34e..000000000 --- a/packages/buildbucket-dart/lib/src/generated/google/rpc/status.pbenum.dart +++ /dev/null @@ -1,10 +0,0 @@ -// -// Generated code. Do not modify. -// source: google/rpc/status.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import diff --git a/packages/buildbucket-dart/lib/src/generated/google/rpc/status.pbjson.dart b/packages/buildbucket-dart/lib/src/generated/google/rpc/status.pbjson.dart deleted file mode 100644 index b5685edcc..000000000 --- a/packages/buildbucket-dart/lib/src/generated/google/rpc/status.pbjson.dart +++ /dev/null @@ -1,29 +0,0 @@ -// -// Generated code. Do not modify. -// source: google/rpc/status.proto -// -// @dart = 2.12 - -// ignore_for_file: annotate_overrides, camel_case_types -// ignore_for_file: constant_identifier_names, library_prefixes -// ignore_for_file: non_constant_identifier_names, prefer_final_fields -// ignore_for_file: unnecessary_import, unnecessary_this, unused_import - -import 'dart:convert' as $convert; -import 'dart:core' as $core; -import 'dart:typed_data' as $typed_data; - -@$core.Deprecated('Use statusDescriptor instead') -const Status$json = { - '1': 'Status', - '2': [ - {'1': 'code', '3': 1, '4': 1, '5': 5, '10': 'code'}, - {'1': 'message', '3': 2, '4': 1, '5': 9, '10': 'message'}, - {'1': 'details', '3': 3, '4': 3, '5': 11, '6': '.google.protobuf.Any', '10': 'details'}, - ], -}; - -/// Descriptor for `Status`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List statusDescriptor = - $convert.base64Decode('CgZTdGF0dXMSEgoEY29kZRgBIAEoBVIEY29kZRIYCgdtZXNzYWdlGAIgASgJUgdtZXNzYWdlEi' - '4KB2RldGFpbHMYAyADKAsyFC5nb29nbGUucHJvdG9idWYuQW55UgdkZXRhaWxz'); diff --git a/packages/buildbucket-dart/pubspec.yaml b/packages/buildbucket-dart/pubspec.yaml deleted file mode 100644 index d1465225d..000000000 --- a/packages/buildbucket-dart/pubspec.yaml +++ /dev/null @@ -1,17 +0,0 @@ -name: buildbucket -description: LUCI Buildbucket library. Protos and utilities to communicate with LUCI buildbucket service. -version: 1.0.6 -repository: https://github.com/flutter/cocoon/tree/main/packages/buildbucket-dart - -environment: - sdk: '>=3.0.0-0 <4.0.0' - -dependencies: - protoc_plugin: 21.1.2 - fixnum: 1.1.0 - protobuf: 3.1.0 - grpc: 3.2.4 - -dev_dependencies: - lints: 3.0.0 - test: 1.24.8 diff --git a/packages/buildbucket-dart/test/buildbucket_pb_test.dart b/packages/buildbucket-dart/test/buildbucket_pb_test.dart deleted file mode 100644 index 0dc928bc9..000000000 --- a/packages/buildbucket-dart/test/buildbucket_pb_test.dart +++ /dev/null @@ -1,20 +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. - -import 'package:buildbucket/buildbucket_pb.dart'; -import 'package:test/test.dart'; - -Map buildJson = { - 'builder': {'project': 'flutter', 'bucket': 'try', 'builder': 'buildabc'} -}; - -void main() { - group('Build proto', () { - test('From json and to json', () { - Build build = Build(); - build.mergeFromProto3Json(buildJson); - expect(build.toProto3Json(), buildJson); - }); - }); -} diff --git a/packages/buildbucket-dart/tool/regenerate.sh b/packages/buildbucket-dart/tool/regenerate.sh deleted file mode 100755 index a6fb85a07..000000000 --- a/packages/buildbucket-dart/tool/regenerate.sh +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env bash -# 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. - -set -ex - -mkdir -p lib/src/generated -rm -rf buildbucket_tmp -mkdir -p buildbucket_tmp - -pushd buildbucket_tmp - -git clone https://chromium.googlesource.com/infra/luci/luci-go -git clone https://github.com/googleapis/googleapis -git clone https://github.com/protocolbuffers/protobuf - -PROTOC="protoc --plugin=protoc-gen-dart=$HOME/.pub-cache/bin/protoc-gen-dart --dart_out=grpc:lib/src/generated -Ibuildbucket_tmp/protobuf/src -Ibuildbucket_tmp/googleapis -Ibuildbucket_tmp/luci-go -Ibuildbucket_tmp/buildbucket" -pushd luci-go -find . -name *.proto -exec bash -c 'path={}; d=../buildbucket/go.chromium.org/luci/$(dirname $path); mkdir -p $d ; cp $path $d' \; -popd -popd -$PROTOC go.chromium.org/luci/buildbucket/proto/build.proto -$PROTOC go.chromium.org/luci/buildbucket/proto/builder_service.proto -$PROTOC go.chromium.org/luci/buildbucket/proto/builds_service.proto -$PROTOC go.chromium.org/luci/buildbucket/proto/builder_common.proto -$PROTOC go.chromium.org/luci/buildbucket/proto/common.proto -$PROTOC go.chromium.org/luci/buildbucket/proto/launcher.proto -$PROTOC go.chromium.org/luci/buildbucket/proto/project_config.proto -$PROTOC go.chromium.org/luci/buildbucket/proto/step.proto -$PROTOC go.chromium.org/luci/buildbucket/proto/task.proto -$PROTOC go.chromium.org/luci/buildbucket/proto/notification.proto -$PROTOC go.chromium.org/luci/resultdb/proto/v1/common.proto -$PROTOC go.chromium.org/luci/resultdb/proto/v1/invocation.proto -$PROTOC go.chromium.org/luci/resultdb/proto/v1/predicate.proto -$PROTOC go.chromium.org/luci/common/proto/structmask/structmask.proto - -$PROTOC google/protobuf/any.proto -$PROTOC google/protobuf/duration.proto -$PROTOC google/protobuf/empty.proto -$PROTOC google/protobuf/struct.proto -$PROTOC google/protobuf/timestamp.proto -$PROTOC google/protobuf/wrappers.proto -$PROTOC google/protobuf/field_mask.proto -$PROTOC google/rpc/status.proto diff --git a/test_utilities/bin/analyze.sh b/test_utilities/bin/analyze.sh deleted file mode 100755 index bdf895f3b..000000000 --- a/test_utilities/bin/analyze.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash -# 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. - -# General flutter/cocoon repo static analysis. - -set -ex - -dir=$(dirname $0) - -pushd $dir/../../analyze > /dev/null -dart pub get -dart --enable-asserts analyze.dart - -popd > /dev/null diff --git a/test_utilities/bin/config_test_runner.sh b/test_utilities/bin/config_test_runner.sh deleted file mode 100755 index 1b2347387..000000000 --- a/test_utilities/bin/config_test_runner.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env bash -# 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. - -# Runner for cocoon scheduler config tests. It expects a single parameter with -# the full path to the config file under test. - -set -ex - -# Build and analyze -echo "Running config tests on $1" -pushd app_dart > /dev/null -flutter clean -pub get - -dart bin/validate_scheduler_config.dart "$1" - -popd > /dev/null diff --git a/test_utilities/bin/dart_test_runner.sh b/test_utilities/bin/dart_test_runner.sh deleted file mode 100755 index eae625130..000000000 --- a/test_utilities/bin/dart_test_runner.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/usr/bin/env bash -# 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. - -# Runner for dart tests. It expects a single parameter with the full -# path to the start folder where tests will be run. - -set -e - -# Build and analyze -echo "###### dart_test_runner #######" -echo "Directory: $1" -pushd "$1" > /dev/null -flutter clean -dart pub get - -# TODO(drewroengoogle): Validate proto code has been generated. https://github.com/flutter/flutter/issues/115473 - -echo "######### dart format #########" -dart format --set-exit-if-changed --line-length=120 . - -echo "########### analyze ###########" -dart analyze --fatal-infos - -# agent doesn't use build_runner as of this writing. -if grep -lq "build_runner" pubspec.yaml; then - echo "############# build ###########" - dart run build_runner build --delete-conflicting-outputs -fi - -# Only try tests if test folder exist. -if [ -d 'test' ]; then - echo "############ tests ############" - dart test --test-randomize-ordering-seed=random --reporter expanded -fi - -INTEGRATION_TEST_DIR="$PWD/integration_test/" -# Only try tests if integration test folder exist. -if [ -d "$INTEGRATION_TEST_DIR" ]; then - echo "###### integration tests ######" - dart test --test-randomize-ordering-seed=random --reporter expanded "$INTEGRATION_TEST_DIR" -fi - -echo "###############################" - -popd > /dev/null diff --git a/test_utilities/bin/flutter_test_runner.sh b/test_utilities/bin/flutter_test_runner.sh deleted file mode 100755 index e6075f293..000000000 --- a/test_utilities/bin/flutter_test_runner.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bash -# 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. - -# This file is used by -# https://github.com/flutter/tests/tree/master/registry/flutter_cocoon.test -# to run the tests of certain packages in this repository as a presubmit -# for the flutter/flutter repository. -# Changes to this file (and any tests in this repository) are only honored -# after the commit hash in the "flutter_cocoon.test" mentioned above has -# been updated. - -# Runner for flutter tests. It expects a single parameter with the full -# path to the flutter project where tests will be run. - -set -ex - -echo "Running flutter tests from $1" -pushd "$1" > /dev/null - -flutter packages get -flutter analyze --no-fatal-infos -dart format --set-exit-if-changed --line-length=120 . lib/ test/ -flutter test --test-randomize-ordering-seed=random --reporter expanded - -popd > /dev/null diff --git a/test_utilities/bin/global_test_runner.dart b/test_utilities/bin/global_test_runner.dart deleted file mode 100755 index 7c5d99f55..000000000 --- a/test_utilities/bin/global_test_runner.dart +++ /dev/null @@ -1,43 +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'; -import 'package:yaml/yaml.dart'; -import "package:path/path.dart"; - -// Runs all the configured tests for cocoon repo. -Future main(List rawArgs) async { - final ArgParser argParser = ArgParser()..addOption('tests-file', abbr: 't', defaultsTo: '../tests.yaml'); - final ArgResults args = argParser.parse(rawArgs); - - // Load tests yaml file. - final File file = File(args['tests-file']); - final doc = loadYaml(file.readAsStringSync()); - // Execute the tests - final String baseDir = normalize(join(dirname(Platform.script.toFilePath()), '..', '..')); - final String prepareScriptPath = join(baseDir, 'test_utilities', 'bin', 'prepare_environment.sh'); - await runShellCommand([prepareScriptPath], 'prepare environment'); - doc['tasks'].forEach((task) async { - final String scriptPath = join(baseDir, task['script']); - final String taskPath = join(baseDir, task['task']); - await runShellCommand([scriptPath, taskPath], task['task']); - }); -} - -Future runShellCommand(List args, String taskName) async { - unawaited( - Process.run('sh', args).then((result) { - stdout.writeln('.. stdout ..'); - stdout.writeln(result.stdout); - stdout.writeln('.. stderr ..'); - stderr.writeln(result.stderr); - if (result.exitCode != 0) { - stderr.writeln('There were failures running tests from $taskName'); - exit(result.exitCode); - } - }), - ); -} diff --git a/test_utilities/bin/licenses.sh b/test_utilities/bin/licenses.sh deleted file mode 100755 index bb1d66392..000000000 --- a/test_utilities/bin/licenses.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/usr/bin/env bash -# 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. - -# Runner for dart tests. It expects a single parameter with the full -# path to the start folder where tests will be run. - -set -ex - -dir=$(dirname $0) - -pushd $dir/../../licenses > /dev/null -dart pub get -dart check_licenses.dart - -popd > /dev/null diff --git a/test_utilities/bin/prepare_environment.sh b/test_utilities/bin/prepare_environment.sh deleted file mode 100755 index 3d5d3dd66..000000000 --- a/test_utilities/bin/prepare_environment.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env bash -# 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. - -# List of commands to be run once per PR before running dart and flutter -# tests. - -set -ex - -dart pub global activate tuneup -flutter channel master -flutter upgrade diff --git a/test_utilities/pubspec.yaml b/test_utilities/pubspec.yaml deleted file mode 100644 index d0042cfed..000000000 --- a/test_utilities/pubspec.yaml +++ /dev/null @@ -1,15 +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. - -name: global_test_runner -description: Flutter continuous integration agent (Cocoon agent) -homepage: https://github.com/flutter/cocoon - -environment: - sdk: '>=2.18.0 < 4.0.0' - -dev_dependencies: - args: 2.4.2 - yaml: 3.1.2 - path: 1.8.3 diff --git a/tests.yaml b/tests.yaml deleted file mode 100644 index ff6a0629d..000000000 --- a/tests.yaml +++ /dev/null @@ -1,48 +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. - -# Tests configuration file. -# -# It is used to define the tests that will be run on this repository -# it uses a very simple schema: -# -# tasks: -# - task: -# script: - -tasks: - # License checks should be first as testing Flutter projects creates artifacts - # from plugins that will fail the license checker. - - task: licenses - script: test_utilities/bin/dart_test_runner.sh - - - task: licenses_check - script: test_utilities/bin/licenses.sh - - - task: analyze - script: test_utilities/bin/analyze.sh - - - task: cipd_packages/codesign - script: test_utilities/bin/dart_test_runner.sh - - - task: app_dart - script: test_utilities/bin/dart_test_runner.sh - - - task: dashboard - script: test_utilities/bin/flutter_test_runner.sh - - - task: auto_submit - script: test_utilities/bin/dart_test_runner.sh - - - task: cipd_packages/device_doctor - script: test_utilities/bin/dart_test_runner.sh - - - task: packages/buildbucket-dart - script: test_utilities/bin/dart_test_runner.sh - - - task: dev/githubanalysis - script: test_utilities/bin/dart_test_runner.sh - - - task: dev/wiki-visualizer - script: test_utilities/bin/dart_test_runner.sh diff --git a/tooling/go.mod b/tooling/go.mod deleted file mode 100644 index 8e84752a5..000000000 --- a/tooling/go.mod +++ /dev/null @@ -1,195 +0,0 @@ -module cocoon-tooling - -go 1.18 - -require github.com/slsa-framework/slsa-verifier/v2 v2.4.1 - -require ( - cloud.google.com/go/compute v1.23.0 // indirect - cloud.google.com/go/compute/metadata v0.2.3 // indirect - filippo.io/edwards25519 v1.0.0 // indirect - github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/alibabacloudsdkgo/helper v0.2.0 // indirect - github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect - github.com/Azure/go-autorest v14.2.0+incompatible // indirect - github.com/Azure/go-autorest/autorest v0.11.29 // indirect - github.com/Azure/go-autorest/autorest/adal v0.9.22 // indirect - github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 // indirect - github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect - github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect - github.com/Azure/go-autorest/logger v0.2.1 // indirect - github.com/Azure/go-autorest/tracing v0.6.0 // indirect - github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903 // indirect - github.com/ThalesIgnite/crypto11 v1.2.5 // indirect - github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 // indirect - github.com/alibabacloud-go/cr-20160607 v1.0.1 // indirect - github.com/alibabacloud-go/cr-20181201 v1.0.10 // indirect - github.com/alibabacloud-go/darabonba-openapi v0.1.18 // indirect - github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68 // indirect - github.com/alibabacloud-go/endpoint-util v1.1.1 // indirect - github.com/alibabacloud-go/openapi-util v0.0.11 // indirect - github.com/alibabacloud-go/tea v1.1.18 // indirect - github.com/alibabacloud-go/tea-utils v1.4.4 // indirect - github.com/alibabacloud-go/tea-xml v1.1.2 // indirect - github.com/aliyun/credentials-go v1.2.3 // indirect - github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect - github.com/aws/aws-sdk-go-v2 v1.20.0 // indirect - github.com/aws/aws-sdk-go-v2/config v1.18.32 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.13.31 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.7 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.37 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.31 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.3.38 // indirect - github.com/aws/aws-sdk-go-v2/service/ecr v1.15.0 // indirect - github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.12.0 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.31 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.13.1 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.1 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.21.1 // indirect - github.com/aws/smithy-go v1.14.0 // indirect - github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220228164355-396b2034c795 // indirect - github.com/blang/semver v3.5.1+incompatible // indirect - github.com/chrismellard/docker-credential-acr-env v0.0.0-20220119192733-fe33c00cee21 // indirect - github.com/clbanning/mxj/v2 v2.5.6 // indirect - github.com/cloudflare/circl v1.3.3 // indirect - github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be // indirect - github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect - github.com/coreos/go-oidc/v3 v3.6.0 // indirect - github.com/cyberphone/json-canonicalization v0.0.0-20220623050100-57a0ce2678a7 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 // indirect - github.com/digitorus/timestamp v0.0.0-20230821155606-d1ad5ca9624c // indirect - github.com/dimchansky/utfbom v1.1.1 // indirect - github.com/docker/cli v24.0.0+incompatible // indirect - github.com/docker/distribution v2.8.2+incompatible // indirect - github.com/docker/docker v24.0.0+incompatible // indirect - github.com/docker/docker-credential-helpers v0.7.0 // indirect - github.com/docker/go v1.5.1-1 // indirect - github.com/emicklei/go-restful/v3 v3.10.2 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.2 // indirect - github.com/go-chi/chi v4.1.2+incompatible // indirect - github.com/go-jose/go-jose/v3 v3.0.0 // indirect - github.com/go-logr/logr v1.2.4 // indirect - github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/analysis v0.21.4 // indirect - github.com/go-openapi/errors v0.20.4 // indirect - github.com/go-openapi/jsonpointer v0.19.6 // indirect - github.com/go-openapi/jsonreference v0.20.2 // indirect - github.com/go-openapi/loads v0.21.2 // indirect - github.com/go-openapi/runtime v0.26.0 // indirect - github.com/go-openapi/spec v0.20.9 // indirect - github.com/go-openapi/strfmt v0.21.7 // indirect - github.com/go-openapi/swag v0.22.4 // indirect - github.com/go-openapi/validate v0.22.1 // indirect - github.com/go-playground/locales v0.14.1 // indirect - github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.15.1 // indirect - github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang-jwt/jwt/v4 v4.5.0 // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/golang/snappy v0.0.4 // indirect - github.com/google/certificate-transparency-go v1.1.6 // indirect - github.com/google/gnostic v0.5.7-v3refs // indirect - github.com/google/go-cmp v0.5.9 // indirect - github.com/google/go-containerregistry v0.16.1 // indirect - github.com/google/go-github/v53 v53.2.0 // indirect - github.com/google/go-querystring v1.1.0 // indirect - github.com/google/gofuzz v1.2.0 // indirect - github.com/google/trillian v1.5.2 // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/hashicorp/go-retryablehttp v0.7.4 // indirect - github.com/hashicorp/hcl v1.0.0 // indirect - github.com/imdario/mergo v0.3.15 // indirect - github.com/in-toto/in-toto-golang v0.9.0 // indirect - github.com/inconshreveable/mousetrap v1.1.0 // indirect - github.com/jedisct1/go-minisign v0.0.0-20211028175153-1c139d1cc84b // indirect - github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/josharian/intern v1.0.0 // indirect - github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.16.5 // indirect - github.com/leodido/go-urn v1.2.4 // indirect - github.com/letsencrypt/boulder v0.0.0-20221109233200-85aa52084eaf // indirect - github.com/magiconair/properties v1.8.7 // indirect - github.com/mailru/easyjson v0.7.7 // indirect - github.com/miekg/pkcs11 v1.1.1 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/mozillazg/docker-credential-acr-helper v0.3.0 // indirect - github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 // indirect - github.com/oklog/ulid v1.3.1 // indirect - github.com/opencontainers/go-digest v1.0.0 // indirect - github.com/opencontainers/image-spec v1.1.0-rc4 // indirect - github.com/opentracing/opentracing-go v1.2.0 // indirect - github.com/pelletier/go-toml/v2 v2.0.8 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/sassoftware/relic v7.2.1+incompatible // indirect - github.com/secure-systems-lab/go-securesystemslib v0.7.0 // indirect - github.com/segmentio/ksuid v1.0.4 // indirect - github.com/shibumi/go-pathspec v1.3.0 // indirect - github.com/sigstore/cosign/v2 v2.2.0 // indirect - github.com/sigstore/fulcio v1.4.0 // indirect - github.com/sigstore/protobuf-specs v0.1.1-0.20230518173429-5ef54068bb53 // indirect - github.com/sigstore/rekor v1.2.2 // indirect - github.com/sigstore/sigstore v1.7.2 // indirect - github.com/sigstore/timestamp-authority v1.1.2 // indirect - github.com/sirupsen/logrus v1.9.3 // indirect - github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect - github.com/slsa-framework/slsa-github-generator v1.4.0 // indirect - github.com/spf13/afero v1.9.5 // indirect - github.com/spf13/cast v1.5.1 // indirect - github.com/spf13/cobra v1.7.0 // indirect - github.com/spf13/jwalterweatherman v1.1.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect - github.com/spf13/viper v1.16.0 // indirect - github.com/subosito/gotenv v1.4.2 // indirect - github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect - github.com/thales-e-security/pool v0.0.2 // indirect - github.com/theupdateframework/go-tuf v0.6.1 // indirect - github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect - github.com/tjfoc/gmsm v1.3.2 // indirect - github.com/transparency-dev/merkle v0.0.2 // indirect - github.com/vbatts/tar-split v0.11.3 // indirect - github.com/xanzy/go-gitlab v0.90.0 // indirect - go.mongodb.org/mongo-driver v1.11.3 // indirect - go.opentelemetry.io/otel v1.16.0 // indirect - go.opentelemetry.io/otel/metric v1.16.0 // indirect - go.opentelemetry.io/otel/trace v1.16.0 // indirect - go.step.sm/crypto v0.35.0 // indirect - go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.25.0 // indirect - golang.org/x/crypto v0.12.0 // indirect - golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect - golang.org/x/mod v0.13.0 // indirect - golang.org/x/net v0.14.0 // indirect - golang.org/x/oauth2 v0.11.0 // indirect - golang.org/x/sync v0.3.0 // indirect - golang.org/x/sys v0.13.0 // indirect - golang.org/x/term v0.11.0 // indirect - golang.org/x/text v0.12.0 // indirect - golang.org/x/time v0.3.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230807174057-1744710a1577 // indirect - google.golang.org/grpc v1.57.0 // indirect - google.golang.org/protobuf v1.31.0 // indirect - gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/square/go-jose.v2 v2.6.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/api v0.27.3 // indirect - k8s.io/apimachinery v0.27.3 // indirect - k8s.io/client-go v0.27.3 // indirect - k8s.io/klog/v2 v2.100.1 // indirect - k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect - k8s.io/utils v0.0.0-20230505201702-9f6742963106 // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/release-utils v0.7.4 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect - sigs.k8s.io/yaml v1.3.0 // indirect -) diff --git a/tooling/go.sum b/tooling/go.sum deleted file mode 100644 index 3f8355baf..000000000 --- a/tooling/go.sum +++ /dev/null @@ -1,1321 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= -cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= -cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.110.6 h1:8uYAkj3YHTP/1iwReuHPxLSbdcyc+dSBbzFMrVwDR6Q= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= -cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= -cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/iam v1.1.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y= -cloud.google.com/go/kms v1.15.1 h1:HUC3fAoepH3RpcQXiJhXWWYizjQ5r7YjI7SO9ZbHf9s= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= -filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= -github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230618160516-e936619f9f18 h1:rd389Q26LMy03gG4anandGFC2LW/xvjga5GezeeaxQk= -github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/alibabacloudsdkgo/helper v0.2.0 h1:8+4G8JaejP8Xa6W46PzJEwisNgBXMvFcz78N6zG/ARw= -github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/alibabacloudsdkgo/helper v0.2.0/go.mod h1:GgeIE+1be8Ivm7Sh4RgwI42aTtC9qrcj+Y9Y6CjJhJs= -github.com/Azure/azure-sdk-for-go v46.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= -github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1 h1:/iHxaJhsFr0+xVFfbMr5vxz848jyiWuIEDhYq3y5odY= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg= -github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.0 h1:yfJe15aSwEQ6Oo6J+gdfdulPNoZ3TEhmbhLIoxZcA+U= -github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v0.8.0 h1:T028gtTPiYt/RMUfs8nVsAL7FDQrfLlrm/NnRG/zcC4= -github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.6/go.mod h1:V6p3pKZx1KKkJubbxnDWrzNhEIfOy/pTGasLqzHIPHs= -github.com/Azure/go-autorest/autorest v0.11.8/go.mod h1:V6p3pKZx1KKkJubbxnDWrzNhEIfOy/pTGasLqzHIPHs= -github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc= -github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw= -github.com/Azure/go-autorest/autorest v0.11.29/go.mod h1:ZtEzC4Jy2JDrZLxvWs8LrBWEBycl1hbT1eknI8MtfAs= -github.com/Azure/go-autorest/autorest/adal v0.9.4/go.mod h1:/3SMAM86bP6wC9Ev35peQDUeqFZBMH07vvUOmg4z/fE= -github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ= -github.com/Azure/go-autorest/autorest/adal v0.9.22 h1:/GblQdIudfEM3AWWZ0mrYJQSd7JS4S/Mbzh6F0ov0Xc= -github.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.2/go.mod h1:q98IH4qgc3eWM4/WOeR5+YPmBuy8Lq0jNRDwSM0CuFk= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 h1:wkAZRgT/pn8HhFyzfe9UnqOjJYqlembgCTi72Bm/xKk= -github.com/Azure/go-autorest/autorest/azure/auth v0.5.12/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.1/go.mod h1:JfDgiIO1/RPu6z42AdQTyjOoCM2MFhLqSBDvMEkDgcg= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.5/go.mod h1:ADQAXrkgm7acgWVUNamOgh8YNrv4p27l3Wc55oVfpzg= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 h1:w77/uPk80ZET2F+AfQExZyEWtn+0Rk/uw17m9fv5Ajc= -github.com/Azure/go-autorest/autorest/azure/cli v0.4.6/go.mod h1:piCfgPho7BiIDdEQ1+g4VmKyD5y+p/XtSNqE6Hc4QD0= -github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw= -github.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU= -github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= -github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= -github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 h1:OBhqkivkhkMqLPymWEppkm7vgPQY2XsHoEkaMQ0AdZY= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903 h1:ZK3C5DtzV2nVAQTx5S5jQvMeDqWtD1By5mOoyY/xJek= -github.com/ProtonMail/go-crypto v0.0.0-20230518184743-7afd39499903/go.mod h1:8TI4H3IbrackdNgv+92dI+rhpCaLqM0IfpgCgenFvRE= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/ThalesIgnite/crypto11 v1.2.5 h1:1IiIIEqYmBvUYFeMnHqRft4bwf/O36jryEUpY+9ef8E= -github.com/ThalesIgnite/crypto11 v1.2.5/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR2E23+wTQe/MlE= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= -github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.2/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc= -github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 h1:iC9YFYKDGEy3n/FtqJnOkZsene9olVspKmkX5A2YBEo= -github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc= -github.com/alibabacloud-go/cr-20160607 v1.0.1 h1:WEnP1iPFKJU74ryUKh/YDPHoxMZawqlPajOymyNAkts= -github.com/alibabacloud-go/cr-20160607 v1.0.1/go.mod h1:QHeKZtZ3F3FOE+/uIXCBAp8POwnUYekpLwr1dtQa5r0= -github.com/alibabacloud-go/cr-20181201 v1.0.10 h1:B60f6S1imsgn2fgC6X6FrVNrONDrbCT0NwYhsJ0C9/c= -github.com/alibabacloud-go/cr-20181201 v1.0.10/go.mod h1:VN9orB/w5G20FjytoSpZROqu9ZqxwycASmGqYUJSoDc= -github.com/alibabacloud-go/darabonba-openapi v0.1.12/go.mod h1:sTAjsFJmVsmcVeklL9d9uDBlFsgl43wZ6jhI6BHqHqU= -github.com/alibabacloud-go/darabonba-openapi v0.1.14/go.mod h1:w4CosR7O/kapCtEEMBm3JsQqWBU/CnZ2o0pHorsTWDI= -github.com/alibabacloud-go/darabonba-openapi v0.1.18 h1:3eUVmAr7WCJp7fgIvmCd9ZUyuwtJYbtUqJIed5eXCmk= -github.com/alibabacloud-go/darabonba-openapi v0.1.18/go.mod h1:PB4HffMhJVmAgNKNq3wYbTUlFvPgxJpTzd1F5pTuUsc= -github.com/alibabacloud-go/darabonba-string v1.0.0/go.mod h1:93cTfV3vuPhhEwGGpKKqhVW4jLe7tDpo3LUM0i0g6mA= -github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68 h1:NqugFkGxx1TXSh/pBcU00Y6bljgDPaFdh5MUSeJ7e50= -github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68/go.mod h1:6pb/Qy8c+lqua8cFpEy7g39NRRqOWc3rOwAy8m5Y2BY= -github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE= -github.com/alibabacloud-go/endpoint-util v1.1.1 h1:ZkBv2/jnghxtU0p+upSU0GGzW1VL9GQdZO3mcSUTUy8= -github.com/alibabacloud-go/endpoint-util v1.1.1/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE= -github.com/alibabacloud-go/openapi-util v0.0.9/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws= -github.com/alibabacloud-go/openapi-util v0.0.10/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws= -github.com/alibabacloud-go/openapi-util v0.0.11 h1:iYnqOPR5hyEEnNZmebGyRMkkEJRWUEjDiiaOHZ5aNhA= -github.com/alibabacloud-go/openapi-util v0.0.11/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws= -github.com/alibabacloud-go/tea v1.1.0/go.mod h1:IkGyUSX4Ba1V+k4pCtJUc6jDpZLFph9QMy2VUPTwukg= -github.com/alibabacloud-go/tea v1.1.7/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= -github.com/alibabacloud-go/tea v1.1.8/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= -github.com/alibabacloud-go/tea v1.1.11/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4= -github.com/alibabacloud-go/tea v1.1.17/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= -github.com/alibabacloud-go/tea v1.1.18 h1:+6GJ06eu5Cr/Mkj09vWrf6QAfrPepctY2OxcWNclRC0= -github.com/alibabacloud-go/tea v1.1.18/go.mod h1:nXxjm6CIFkBhwW4FQkNrolwbfon8Svy6cujmKFUq98A= -github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE= -github.com/alibabacloud-go/tea-utils v1.3.9/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE= -github.com/alibabacloud-go/tea-utils v1.4.3/go.mod h1:KNcT0oXlZZxOXINnZBs6YvgOd5aYp9U67G+E3R8fcQw= -github.com/alibabacloud-go/tea-utils v1.4.4 h1:lxCDvNCdTo9FaXKKq45+4vGETQUKNOW/qKTcX9Sk53o= -github.com/alibabacloud-go/tea-utils v1.4.4/go.mod h1:KNcT0oXlZZxOXINnZBs6YvgOd5aYp9U67G+E3R8fcQw= -github.com/alibabacloud-go/tea-xml v1.1.2 h1:oLxa7JUXm2EDFzMg+7oRsYc+kutgCVwm+bZlhhmvW5M= -github.com/alibabacloud-go/tea-xml v1.1.2/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8= -github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw= -github.com/aliyun/credentials-go v1.2.3 h1:Vmodnr52Rz1mcbwn0kzMhLRKb6soizewuKXdfZiNemU= -github.com/aliyun/credentials-go v1.2.3/go.mod h1:/KowD1cfGSLrLsH28Jr8W+xwoId0ywIy5lNzDz6O1vw= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= -github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.44.318 h1:Yl66rpbQHFUbxe9JBKLcvOvRivhVgP6+zH0b9KzARX8= -github.com/aws/aws-sdk-go-v2 v1.7.1/go.mod h1:L5LuPC1ZgDr2xQS7AmIec/Jlc7O/Y1u2KxJyNVab250= -github.com/aws/aws-sdk-go-v2 v1.14.0/go.mod h1:ZA3Y8V0LrlWj63MQAnRHgKf/5QB//LSZCPNWlWrNGLU= -github.com/aws/aws-sdk-go-v2 v1.20.0 h1:INUDpYLt4oiPOJl0XwZDK2OVAVf0Rzo+MGVTv9f+gy8= -github.com/aws/aws-sdk-go-v2 v1.20.0/go.mod h1:uWOr0m0jDsiWw8nnXiqZ+YG6LdvAlGYDLLf2NmHZoy4= -github.com/aws/aws-sdk-go-v2/config v1.5.0/go.mod h1:RWlPOAW3E3tbtNAqTwvSW54Of/yP3oiZXMI0xfUdjyA= -github.com/aws/aws-sdk-go-v2/config v1.18.32 h1:tqEOvkbTxwEV7hToRcJ1xZRjcATqwDVsWbAscgRKyNI= -github.com/aws/aws-sdk-go-v2/config v1.18.32/go.mod h1:U3ZF0fQRRA4gnbn9GGvOWLoT2EzzZfAWeKwnVrm1rDc= -github.com/aws/aws-sdk-go-v2/credentials v1.3.1/go.mod h1:r0n73xwsIVagq8RsxmZbGSRQFj9As3je72C2WzUIToc= -github.com/aws/aws-sdk-go-v2/credentials v1.13.31 h1:vJyON3lG7R8VOErpJJBclBADiWTwzcwdkQpTKx8D2sk= -github.com/aws/aws-sdk-go-v2/credentials v1.13.31/go.mod h1:T4sESjBtY2lNxLgkIASmeP57b5j7hTQqCbqG0tWnxC4= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.3.0/go.mod h1:2LAuqPx1I6jNfaGDucWfA2zqQCYCOMCDHiCOciALyNw= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.7 h1:X3H6+SU21x+76LRglk21dFRgMTJMa5QcpW+SqUf5BBg= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.7/go.mod h1:3we0V09SwcJBzNlnyovrR2wWJhWmVdqAsmVs4uronv8= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.5/go.mod h1:2hXc8ooJqF2nAznsbJQIn+7h851/bu8GVC80OVTTqf8= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.37 h1:zr/gxAZkMcvP71ZhQOcvdm8ReLjFgIXnIn0fw5AM7mo= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.37/go.mod h1:Pdn4j43v49Kk6+82spO3Tu5gSeQXRsxo56ePPQAvFiA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.3.0/go.mod h1:miRSv9l093jX/t/j+mBCaLqFHo9xKYzJ7DGm1BsGoJM= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.31 h1:0HCMIkAkVY9KMgueD8tf4bRTUanzEYvhw7KkPXIMpO0= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.31/go.mod h1:fTJDMe8LOFYtqiFFFeHA+SVMAwqLhoq0kcInYoLa9Js= -github.com/aws/aws-sdk-go-v2/internal/ini v1.1.1/go.mod h1:Zy8smImhTdOETZqfyn01iNOe0CNggVbPjCajyaz6Gvg= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.38 h1:+i1DOFrW3YZ3apE45tCal9+aDKK6kNEbW6Ib7e1nFxE= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.38/go.mod h1:1/jLp0OgOaWIetycOmycW+vYTYgTZFPttJQRgsI1PoU= -github.com/aws/aws-sdk-go-v2/service/ecr v1.4.1/go.mod h1:FglZcyeiBqcbvyinl+n14aT/EWC7S1MIH+Gan2iizt0= -github.com/aws/aws-sdk-go-v2/service/ecr v1.15.0 h1:lY2Z2sBP+zSbJ6CvvmnFgPcgknoQ0OJV88AwVetRRFk= -github.com/aws/aws-sdk-go-v2/service/ecr v1.15.0/go.mod h1:4zYI85WiYDhFaU1jPFVfkD7HlBcdnITDE3QxDwy4Kus= -github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.4.1/go.mod h1:eD5Eo4drVP2FLTw0G+SMIPWNWvQRGGTtIZR2XeAagoA= -github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.12.0 h1:LsqBpyRofMG6eDs6YGud6FhdGyIyXelAasPOZ6wWLro= -github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.12.0/go.mod h1:IArQ3IBR00FkuraKwudKZZU32OxJfdTdwV+W5iZh3Y4= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.2.1/go.mod h1:zceowr5Z1Nh2WVP8bf/3ikB41IZW59E4yIYbg+pC6mw= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.31 h1:auGDJ0aLZahF5SPvkJ6WcUuX7iQ7kyl2MamV7Tm8QBk= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.31/go.mod h1:3+lloe3sZuBQw1aBc5MyndvodzQlyqCZ7x1QPDHaWP4= -github.com/aws/aws-sdk-go-v2/service/kms v1.24.1 h1:zDmx9yZjSYDaeakQVN16qfsLxhBeAxgclioB0+rOCDM= -github.com/aws/aws-sdk-go-v2/service/sso v1.3.1/go.mod h1:J3A3RGUvuCZjvSuZEcOpHDnzZP/sKbhDWV2T1EOzFIM= -github.com/aws/aws-sdk-go-v2/service/sso v1.13.1 h1:DSNpSbfEgFXRV+IfEcKE5kTbqxm+MeF5WgyeRlsLnHY= -github.com/aws/aws-sdk-go-v2/service/sso v1.13.1/go.mod h1:TC9BubuFMVScIU+TLKamO6VZiYTkYoEHqlSQwAe2omw= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.1 h1:hd0SKLMdOL/Sl6Z0np1PX9LeH2gqNtBe0MhTedA8MGI= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.1/go.mod h1:XO/VcyoQ8nKyKfFW/3DMsRQXsfh/052tHTWmg3xBXRg= -github.com/aws/aws-sdk-go-v2/service/sts v1.6.0/go.mod h1:q7o0j7d7HrJk/vr9uUt3BVRASvcU7gYZB9PUgPiByXg= -github.com/aws/aws-sdk-go-v2/service/sts v1.21.1 h1:pAOJj+80tC8sPVgSDHzMYD6KLWsaLQ1kZw31PTeORbs= -github.com/aws/aws-sdk-go-v2/service/sts v1.21.1/go.mod h1:G8SbvL0rFk4WOJroU8tKBczhsbhj2p/YY7qeJezJ3CI= -github.com/aws/smithy-go v1.6.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= -github.com/aws/smithy-go v1.11.0/go.mod h1:3xHYmszWVx2c0kIwQeEVf9uSm4fYZt67FBJnwub1bgM= -github.com/aws/smithy-go v1.14.0 h1:+X90sB94fizKjDmwb4vyl2cTTPXTE5E2G/1mjByb0io= -github.com/aws/smithy-go v1.14.0/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= -github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220228164355-396b2034c795 h1:IWeCJzU+IYaO2rVEBlGPTBfe90cmGXFTLdhUFlzDGsY= -github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220228164355-396b2034c795/go.mod h1:8vJsEZ4iRqG+Vx6pKhWK6U00qcj0KC37IsfszMkY6UE= -github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= -github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= -github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= -github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/chrismellard/docker-credential-acr-env v0.0.0-20220119192733-fe33c00cee21 h1:XlpL9EHrPOBJMLDDOf35/G4t5rGAFNNAZQ3cDcWavtc= -github.com/chrismellard/docker-credential-acr-env v0.0.0-20220119192733-fe33c00cee21/go.mod h1:Zlre/PVxuSI9y6/UV4NwGixQ48RHQDSPiUkofr6rbMU= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/clbanning/mxj/v2 v2.5.6 h1:Jm4VaCI/+Ug5Q57IzEoZbwx4iQFA6wkXv72juUSeK+g= -github.com/clbanning/mxj/v2 v2.5.6/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= -github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= -github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE= -github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be h1:J5BL2kskAlV9ckgEsNQXscjIaLiOYiZ75d4e94E6dcQ= -github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be/go.mod h1:mk5IQ+Y0ZeO87b858TlA645sVcEcbiX6YqP98kt+7+w= -github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k= -github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-oidc/v3 v3.6.0 h1:AKVxfYw1Gmkn/w96z0DbT/B/xFnzTd3MkZvWLjF4n/o= -github.com/coreos/go-oidc/v3 v3.6.0/go.mod h1:ZpHUsHBucTUj6WOkrP4E20UPynbLZzhTQ1XKCXkxyPc= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/cyberphone/json-canonicalization v0.0.0-20220623050100-57a0ce2678a7 h1:vU+EP9ZuFUCYE0NYLwTSob+3LNEJATzNfP/DC7SWGWI= -github.com/cyberphone/json-canonicalization v0.0.0-20220623050100-57a0ce2678a7/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw= -github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/depcheck-test/depcheck-test v0.0.0-20220607135614-199033aaa936 h1:foGzavPWwtoyBvjWyKJYDYsyzy+23iBV7NKTwdk+LRY= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/digitorus/pkcs7 v0.0.0-20230713084857-e76b763bdc49/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc= -github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 h1:ge14PCmCvPjpMQMIAH7uKg0lrtNSOdpYsRXlwk3QbaE= -github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc= -github.com/digitorus/timestamp v0.0.0-20230821155606-d1ad5ca9624c h1:kgG83Hfj3YXkUbrihwBxDc0COzP1ZejiDSr4/fItT0E= -github.com/digitorus/timestamp v0.0.0-20230821155606-d1ad5ca9624c/go.mod h1:GvWntX9qiTlOud0WkQ6ewFm0LPy5JUR1Xo0Ngbd1w6Y= -github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= -github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= -github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= -github.com/docker/cli v24.0.0+incompatible h1:0+1VshNwBQzQAx9lOl+OYCTCEAD8fKs/qeXMx3O0wqM= -github.com/docker/cli v24.0.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= -github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v24.0.0+incompatible h1:z4bf8HvONXX9Tde5lGBMQ7yCJgNahmJumdrStZAbeY4= -github.com/docker/docker v24.0.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= -github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= -github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= -github.com/docker/go v1.5.1-1 h1:hr4w35acWBPhGBXlzPoHpmZ/ygPjnmFVxGxxGnMyP7k= -github.com/docker/go v1.5.1-1/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/emicklei/go-restful/v3 v3.10.2 h1:hIovbnmBTLjHXkqEBUz3HGpXZdM7ZrE9fJIZIqlJLqE= -github.com/emicklei/go-restful/v3 v3.10.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= -github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/facebookgo/clock v0.0.0-20150410010913-600d898af40a h1:yDWHCSQ40h88yih2JAcL6Ls/kVkSE8GFACTGVnMPruw= -github.com/facebookgo/limitgroup v0.0.0-20150612190941-6abd8d71ec01 h1:IeaD1VDVBPlx3viJT9Md8if8IxxJnO+x0JCGb054heg= -github.com/facebookgo/muster v0.0.0-20150708232844-fd3d7953fd52 h1:a4DFiKFJiDRGFD1qIcqGLX/WlUMD9dyLSLDt+9QZgt8= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w= -github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= -github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec= -github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo= -github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY= -github.com/go-openapi/analysis v0.21.4 h1:ZDFLvSNxpDaomuCueM0BlSXxpANBlFYiBvr+GXrvIHc= -github.com/go-openapi/analysis v0.21.4/go.mod h1:4zQ35W4neeZTqh3ol0rv/O8JBbka9QyAgQRPp9y3pfo= -github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= -github.com/go-openapi/errors v0.20.4 h1:unTcVm6PispJsMECE3zWgvG4xTiKda1LIR5rCRWLG6M= -github.com/go-openapi/errors v0.20.4/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= -github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= -github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= -github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= -github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/loads v0.21.1/go.mod h1:/DtAMXXneXFjbQMGEtbamCZb+4x7eGwkvZCvBmwUG+g= -github.com/go-openapi/loads v0.21.2 h1:r2a/xFIYeZ4Qd2TnGpWDIQNcP80dIaZgf704za8enro= -github.com/go-openapi/loads v0.21.2/go.mod h1:Jq58Os6SSGz0rzh62ptiu8Z31I+OTHqmULx5e/gJbNw= -github.com/go-openapi/runtime v0.26.0 h1:HYOFtG00FM1UvqrcxbEJg/SwvDRvYLQKGhw2zaQjTcc= -github.com/go-openapi/runtime v0.26.0/go.mod h1:QgRGeZwrUcSHdeh4Ka9Glvo0ug1LC5WyE+EV88plZrQ= -github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= -github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= -github.com/go-openapi/spec v0.20.9 h1:xnlYNQAwKd2VQRRfwTEI0DcK+2cbuvI/0c7jx3gA8/8= -github.com/go-openapi/spec v0.20.9/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= -github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg= -github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= -github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg= -github.com/go-openapi/strfmt v0.21.7 h1:rspiXgNWgeUzhjo1YU01do6qsahtJNByjLVbPLNHb8k= -github.com/go-openapi/strfmt v0.21.7/go.mod h1:adeGTkxE44sPyLk0JV235VQAO/ZXUr8KAzYjclFs3ew= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= -github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/validate v0.22.1 h1:G+c2ub6q47kfX1sOBLwIQwzBVt8qmOAARyo/9Fqs9NU= -github.com/go-openapi/validate v0.22.1/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= -github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= -github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= -github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= -github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= -github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.15.1 h1:BSe8uhN+xQ4r5guV/ywQI4gO59C2raYcGffYWZEjZzM= -github.com/go-playground/validator/v10 v10.15.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= -github.com/go-rod/rod v0.114.2 h1:Qwt+vZHHnb117zc0q+XjhAJCkB01hchWSxH/raCyLb4= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= -github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= -github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= -github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= -github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= -github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= -github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= -github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= -github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= -github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= -github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= -github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= -github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= -github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= -github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= -github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= -github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= -github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= -github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= -github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= -github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= -github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= -github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= -github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/certificate-transparency-go v1.1.6 h1:SW5K3sr7ptST/pIvNkSVWMiJqemRmkjJPPT0jzXdOOY= -github.com/google/certificate-transparency-go v1.1.6/go.mod h1:0OJjOsOk+wj6aYQgP7FU0ioQ0AJUmnWPFMqTjQeazPQ= -github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= -github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-containerregistry v0.16.1 h1:rUEt426sR6nyrL3gt+18ibRcvYpKYdpsa5ZW7MA08dQ= -github.com/google/go-containerregistry v0.16.1/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ= -github.com/google/go-github/v53 v53.2.0 h1:wvz3FyF53v4BK+AsnvCmeNhf8AkTaeh2SoYu/XUvTtI= -github.com/google/go-github/v53 v53.2.0/go.mod h1:XhFRObz+m/l+UCm9b7KSIC3lT3NWSXGt7mOsAWEloao= -github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= -github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= -github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20221103000818-d260c55eee4c h1:lvddKcYTQ545ADhBujtIJmqQrZBDsGo7XIMbAQe/sNY= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/s2a-go v0.1.5 h1:8IYp3w9nysqv3JH+NJgXJzGbDHzLOTj43BmSkp+O7qg= -github.com/google/tink/go v1.7.0 h1:6Eox8zONGebBFcCBqkVmt60LaWZa6xg1cl/DwAh/J1w= -github.com/google/trillian v1.5.2 h1:roGP6G8aaAch7vP08+oitPkvmZzxjTfIkguozqJ04Ok= -github.com/google/trillian v1.5.2/go.mod h1:H8vOoa2dxd3xCdMzOOwt9kIz/3MSoJhcqLJGG8iRwbg= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.2.5 h1:UR4rDjcgpgEnqpIEvkiqTYKBCKLNmlge2eVjoZfySzM= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-hclog v1.2.0 h1:La19f8d7WIlm4ogzNHB0JGqs5AUDAZ2UfCY4sJXcJdM= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA= -github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= -github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 h1:UpiO20jno/eV1eVZcxqWnUohyKRe1g8FPV/xH1s/2qs= -github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hashicorp/vault/api v1.9.2 h1:YjkZLJ7K3inKgMZ0wzCU9OHqc+UqMQyXsPXnf3Cl2as= -github.com/honeycombio/beeline-go v1.10.0 h1:cUDe555oqvw8oD76BQJ8alk7FP0JZ/M/zXpNvOEDLDc= -github.com/honeycombio/libhoney-go v1.16.0 h1:kPpqoz6vbOzgp7jC6SR7SkNj7rua7rgxvznI6M3KdHc= -github.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef h1:A9HsByNhogrvm9cWb28sjiS3i7tcKCkflWFEkHfuAgM= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= -github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= -github.com/in-toto/in-toto-golang v0.9.0 h1:tHny7ac4KgtsfrG6ybU8gVOZux2H8jN05AXJ9EBM1XU= -github.com/in-toto/in-toto-golang v0.9.0/go.mod h1:xsBVrVsHNsB61++S6Dy2vWosKhuA3lUTQd+eF9HdeMo= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= -github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jedisct1/go-minisign v0.0.0-20211028175153-1c139d1cc84b h1:ZGiXF8sz7PDk6RgkP+A/SFfUD0ZR/AgG6SpRNEDKZy8= -github.com/jedisct1/go-minisign v0.0.0-20211028175153-1c139d1cc84b/go.mod h1:hQmNrgofl+IY/8L+n20H6E6PWBBTokdsv+q49j0QhsU= -github.com/jellydator/ttlcache/v3 v3.0.1 h1:cHgCSMS7TdQcoprXnWUptJZzyFsqs18Lt8VVhRuZYVU= -github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/jmhodges/clock v0.0.0-20160418191101-880ee4c33548 h1:dYTbLf4m0a5u0KLmPfB6mgxbcV7588bOCx79hxa5Sr4= -github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= -github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= -github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= -github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= -github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= -github.com/letsencrypt/boulder v0.0.0-20221109233200-85aa52084eaf h1:ndns1qx/5dL43g16EQkPV/i8+b3l5bYQwLeoSBe7tS8= -github.com/letsencrypt/boulder v0.0.0-20221109233200-85aa52084eaf/go.mod h1:aGkAgvWY/IUcVFfuly53REpfv5edu25oij+qHRFaraA= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= -github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= -github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= -github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU= -github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= -github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= -github.com/mozillazg/docker-credential-acr-helper v0.3.0 h1:DVWFZ3/O8BP6Ue3iS/Olw+G07u1hCq1EOVCDZZjCIBI= -github.com/mozillazg/docker-credential-acr-helper v0.3.0/go.mod h1:cZlu3tof523ujmLuiNUb6JsjtHcNA70u1jitrrdnuyA= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 h1:Up6+btDp321ZG5/zdSLo48H9Iaq0UQGthrhWC6pCxzE= -github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481/go.mod h1:yKZQO8QE2bHlgozqWDiRVqTFlLQSj30K/6SAK8EeYFw= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= -github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= -github.com/onsi/ginkgo/v2 v2.9.1 h1:zie5Ly042PD3bsCvsSOPvRnFwyo3rKe64TJlD6nu0mk= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= -github.com/onsi/gomega v1.27.4 h1:Z2AnStgsdSayCMDiCU42qIz+HLqEPcgiOCXjAU/w+8E= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= -github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= -github.com/opencontainers/image-spec v1.1.0-rc4 h1:oOxKUJWnFC4YGHCCMNql1x4YaDfYBTS5Y4x/Cgeo1E0= -github.com/opencontainers/image-spec v1.1.0-rc4/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= -github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= -github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= -github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= -github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= -github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= -github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= -github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= -github.com/sassoftware/relic v7.2.1+incompatible h1:Pwyh1F3I0r4clFJXkSI8bOyJINGqpgjJU3DYAZeI05A= -github.com/sassoftware/relic v7.2.1+incompatible/go.mod h1:CWfAxv73/iLZ17rbyhIEq3K9hs5w6FpNMdUT//qR+zk= -github.com/sassoftware/relic/v7 v7.5.5 h1:2ZUM6ovo3STCAp0hZnO9nQY9lOB8OyfneeYIi4YUxMU= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/secure-systems-lab/go-securesystemslib v0.7.0 h1:OwvJ5jQf9LnIAS83waAjPbcMsODrTQUpJ02eNLUoxBg= -github.com/secure-systems-lab/go-securesystemslib v0.7.0/go.mod h1:/2gYnlnHVQ6xeGtfIqFy7Do03K4cdCY0A/GlJLDKLHI= -github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c= -github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE= -github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI= -github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sigstore/cosign/v2 v2.2.0 h1:MV/ALD1/e/JgxXXCdCNxlIRk2NB3Irb4MKPozd8SPR8= -github.com/sigstore/cosign/v2 v2.2.0/go.mod h1:Kcm7lTZbpiEpA3wPCqRygTUdLpX8CNT+36rODTCBr1M= -github.com/sigstore/fulcio v1.4.0 h1:05+k8BFvwTQzfCkVxESWzCN4b70KIRliGYz0Upmdrs8= -github.com/sigstore/fulcio v1.4.0/go.mod h1:wcjlktbhoy6+ZTxO3yXpvqUxsLV+JEH4FF3a5Jz4VPI= -github.com/sigstore/protobuf-specs v0.1.1-0.20230518173429-5ef54068bb53 h1:q2rO7GOmez7Im4t1JGKCe9BaepE97yMT6NE5zpB3LaA= -github.com/sigstore/protobuf-specs v0.1.1-0.20230518173429-5ef54068bb53/go.mod h1:5shUCxf82hGnjUEFVWiktcxwzdtn6EfeeJssxZ5Q5HE= -github.com/sigstore/rekor v1.2.2 h1:5JK/zKZvcQpL/jBmHvmFj3YbpDMBQnJQ6ygp8xdF3bY= -github.com/sigstore/rekor v1.2.2/go.mod h1:FGnWBGWzeNceJnp0x9eDFd41mI8aQqCjj+Zp0IEs0Qg= -github.com/sigstore/sigstore v1.7.2 h1:MY0wSOhKWa8SIWSCO9SzFnUl+b7jbthgXHJpuUg31Qs= -github.com/sigstore/sigstore v1.7.2/go.mod h1:2IPD5YXrXoznfnIoVsDF7ARC1Nha8xIdLpsC4kEQh5w= -github.com/sigstore/sigstore/pkg/signature/kms/aws v1.7.2 h1:JtdEI/81xugNfHqHyK1icPtnjgrLHJmWm3fgUBhyFPw= -github.com/sigstore/sigstore/pkg/signature/kms/azure v1.7.2 h1:WtTn0OYxbSPY/xEy13iSPjJnSHXhILmzbfleg9x8CuQ= -github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.7.2 h1:/c9AZS8mfOlpjCuBA1aBesuDxPMEN2QtmZS/CQ6Cz8I= -github.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.7.2 h1:GquAXZRIVJ8e10NpTzW4wNkpRGaDdJdPWz2Xlsdc2OM= -github.com/sigstore/timestamp-authority v1.1.2 h1:xgBs9Ct27sEgFb35GL1trKD2XOgYbtk0q2G0HLZHDDY= -github.com/sigstore/timestamp-authority v1.1.2/go.mod h1:7rGe/e6ZJNMqPiwFiv7w+qNXT8GID9gL7nMcRwZ007I= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA= -github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= -github.com/slsa-framework/slsa-github-generator v1.4.0 h1:WiD4MDsSvVE2S8frGNBgb7Suy1wdIVFgY329LlvkXKo= -github.com/slsa-framework/slsa-github-generator v1.4.0/go.mod h1:JVYHnGjS/RMuw3FCqB6U/N+yg80H6aWiKD5yaSGm1no= -github.com/slsa-framework/slsa-verifier/v2 v2.4.1 h1:vpfGcM5+OpXCh7x1to8y87qK2dAMA+6fPWuXNHjIKSQ= -github.com/slsa-framework/slsa-verifier/v2 v2.4.1/go.mod h1:Nd1fnzdsFJctbb8U85Ogbzmvf56U0CZjXHMOqjwIjFc= -github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1/1fApl1A+9VcBk+9dcqGfnePY87LY= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.4.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= -github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= -github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc= -github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= -github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= -github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d h1:vfofYNRScrDdvS342BElfbETmL1Aiz3i2t0zfRj16Hs= -github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= -github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gtvVDbmPg= -github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU= -github.com/theupdateframework/go-tuf v0.6.1 h1:6J89fGjQf7s0mLmTG7p7pO/MbKOg+bIXhaLyQdmbKuE= -github.com/theupdateframework/go-tuf v0.6.1/go.mod h1:LAFusuQsFNBnEyYoTuA5zZrF7iaQ4TEgBXm8lb6Vj18= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= -github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0= -github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs= -github.com/tjfoc/gmsm v1.3.2 h1:7JVkAn5bvUJ7HtU08iW6UiD+UTmJTIToHCfeFzkcCxM= -github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/transparency-dev/merkle v0.0.2 h1:Q9nBoQcZcgPamMkGn7ghV8XiTZ/kRxn1yCG81+twTK4= -github.com/transparency-dev/merkle v0.0.2/go.mod h1:pqSy+OXefQ1EDUVmAJ8MUhHB9TXGuzVAT58PqBoHz1A= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= -github.com/vbatts/tar-split v0.11.3 h1:hLFqsOLQ1SsppQNTMpkpPXClLDfC2A3Zgy9OUU+RVck= -github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY= -github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU= -github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g= -github.com/xanzy/go-gitlab v0.90.0 h1:j8ZUHfLfXdnC+B8njeNaW/kM44c1zw8fiuNj7D+qQN8= -github.com/xanzy/go-gitlab v0.90.0/go.mod h1:5ryv+MnpZStBH8I/77HuQBsMbBGANtVpLWC15qOjWAw= -github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= -github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= -github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= -github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= -github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= -github.com/ysmood/fetchup v0.2.3 h1:ulX+SonA0Vma5zUFXtv52Kzip/xe7aj4vqT5AJwQ+ZQ= -github.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ= -github.com/ysmood/got v0.34.1 h1:IrV2uWLs45VXNvZqhJ6g2nIhY+pgIG1CUoOcqfXFl1s= -github.com/ysmood/gson v0.7.3 h1:QFkWbTH8MxyUTKPkVWAENJhxqdBa4lYTQWqZCiLG6kE= -github.com/ysmood/leakless v0.8.0 h1:BzLrVoiwxikpgEQR0Lk8NyBN5Cit2b1z+u0mgL4ZJak= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/zalando/go-keyring v0.2.2 h1:f0xmpYiSrHtSNAVgwip93Cg8tuF45HJM6rHq/A5RI/4= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= -go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= -go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8= -go.mongodb.org/mongo-driver v1.11.3 h1:Ql6K6qYHEzB6xvu4+AU0BoRoqf9vFPcc4o7MUIdPW8Y= -go.mongodb.org/mongo-driver v1.11.3/go.mod h1:PTSz5yu21bkT/wXpkS7WR5f0ddqw5quethTUn9WM+2g= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= -go.opentelemetry.io/otel v1.16.0 h1:Z7GVAX/UkAXPKsy94IU+i6thsQS4nb7LviLpnaNeW8s= -go.opentelemetry.io/otel v1.16.0/go.mod h1:vl0h9NUa1D5s1nv3A5vZOYWn8av4K8Ml6JDeHrT/bx4= -go.opentelemetry.io/otel/metric v1.16.0 h1:RbrpwVG1Hfv85LgnZ7+txXioPDoh6EdbZHo26Q3hqOo= -go.opentelemetry.io/otel/metric v1.16.0/go.mod h1:QE47cpOmkwipPiefDwo2wDzwJrlfxxNYodqc4xnGCo4= -go.opentelemetry.io/otel/sdk v1.16.0 h1:Z1Ok1YsijYL0CSJpHt4cS3wDDh7p572grzNrBMiMWgE= -go.opentelemetry.io/otel/trace v1.16.0 h1:8JRpaObFoW0pxuVPapkgH8UhHQj+bJW8jJsCZEu5MQs= -go.opentelemetry.io/otel/trace v1.16.0/go.mod h1:Yt9vYq1SdNz3xdjZZK7wcXv1qv2pwLkqr2QVwea0ef0= -go.step.sm/crypto v0.35.0 h1:0N6ks5n1sdv4+biJMUTdqHjpTBKKN9zNqqBdOJIyHe4= -go.step.sm/crypto v0.35.0/go.mod h1:sBsrpVReoxmiLexbWL+vQRxZd6Gq4YBj/IRSUH+DZe4= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c= -go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= -golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= -golang.org/x/mod v0.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU= -golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= -golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201005172224-997123666555/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0= -golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200509030707-2212a7e161a5/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= -golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= -google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= -google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= -google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.138.0 h1:K/tVp05MxNVbHShRw9m7e9VJGdagNeTdMzqPH7AUqr0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWofoFcihmDEMYeDR9KN/ThbPWGrh++g= -google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8= -google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5 h1:nIgk/EEq3/YlnmVVXVnm14rC2oxgs1o0ong4sD/rd44= -google.golang.org/genproto/googleapis/api v0.0.0-20230803162519-f966b187b2e5/go.mod h1:5DZzOUPCLYL3mNkQ0ms0F3EuUNZ7py1Bqeq6sxzI7/Q= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230807174057-1744710a1577 h1:wukfNtZmZUurLN/atp2hiIeTKn7QJWIQdHzqmsOnAOk= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230807174057-1744710a1577/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= -google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.57.0 h1:kfzNeI/klCGD2YPMUlaGNT3pxvYfga7smW3Vth8Zsiw= -google.golang.org/grpc v1.57.0/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/alexcesaro/statsd.v2 v2.0.0 h1:FXkZSCZIH17vLCO5sO2UucTHsH9pc+17F6pl3JVCwMc= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI= -gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.5/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.27.3 h1:yR6oQXXnUEBWEWcvPWS0jQL575KoAboQPfJAuKNrw5Y= -k8s.io/api v0.27.3/go.mod h1:C4BNvZnQOF7JA/0Xed2S+aUyJSfTGkGFxLXz9MnpIpg= -k8s.io/apimachinery v0.27.3 h1:Ubye8oBufD04l9QnNtW05idcOe9Z3GQN8+7PqmuVcUM= -k8s.io/apimachinery v0.27.3/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= -k8s.io/client-go v0.27.3 h1:7dnEGHZEJld3lYwxvLl7WoehK6lAq7GvgjxpA3nv1E8= -k8s.io/client-go v0.27.3/go.mod h1:2MBEKuTo6V1lbKy3z1euEGnhPfGZLKTS9tiJ2xodM48= -k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= -k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= -k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= -k8s.io/utils v0.0.0-20230505201702-9f6742963106 h1:EObNQ3TW2D+WptiYXlApGNLVy0zm/JIBVY9i+M4wpAU= -k8s.io/utils v0.0.0-20230505201702-9f6742963106/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/release-utils v0.7.4 h1:17LmJrydpUloTCtaoWj95uKlcrUp4h2A9Sa+ZL+lV9w= -sigs.k8s.io/release-utils v0.7.4/go.mod h1:JEt2QPHItd5Pg2UKLAU8PEaSlF4bUjCZimpxFDgymVU= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= -software.sslmate.com/src/go-pkcs12 v0.2.0 h1:nlFkj7bTysH6VkC4fGphtjXRbezREPgrHuJG20hBGPE= diff --git a/tooling/tooling_test.go b/tooling/tooling_test.go deleted file mode 100644 index bdf7461da..000000000 --- a/tooling/tooling_test.go +++ /dev/null @@ -1,16 +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. - -// This file is used so that slsa-verifier can be updated by dependabot. -// Additional context can be found at: -// https://github.com/slsa-framework/slsa-verifier#option-1-install-via-go - -//go:build tools -// +build tools - -package main - -import ( - _ "github.com/slsa-framework/slsa-verifier/v2/cli/slsa-verifier" -)

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 diff --git a/dashboard/test/widgets/goldens/task_grid_test.differentTypes.png b/dashboard/test/widgets/goldens/task_grid_test.differentTypes.png deleted file mode 100644 index 2e7ea769aa95e67e03de00e839bac2e8e229111f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3555 zcmeHIeLU3J8b9N0EJbsr5s_t8w`o(YSmbrR^D#GJSPYpi(TiP%c|)^GtK=m`mkh1f zYspAPG$U-1i4kdEYrBfU+?FN9AU(`;3xT+4fyf$$5COe9PVY>EoJTJe=p(Z)R5 zt(53V+IG=|?1v4+(ta!L6muD+bZOZR0mbv194Az&oO%IA%v~y$FJ3{+*^AxY>&=>8?Ip@wjTi?5*DzWpkn6k}e9IH@B?6ic1JuwuD&&N{ z!w~@Lk&VUxIPTN<<4`|cp0CC8Vn;)1%&G@Mr~HQyk3<;K_3!62zgu2;`PAEBGO(8W z_Cal6t$~!kfJ+hPYxTwT*zUD^iV@7R3pq<=bk+c89iX1@K<WlU2W zWob29zWV+hD*wT&j0`T9D}>G!oUpuFIx^h~1AB_|bd7~b?D>{}ob>ec^}Oxdww0~? z;yrl#bs&a>!{Kg3KP<`qpfoi-4!|DcWe)Cs&%`N|gIE%?mNlhqbk4w3g72fOKD*fR zW>r`(J)Z=5WPkIcmf?s+yPb}X4m?i4vbNs0Z{N*;1Z5CGG^41_=!~sm(YRo(t@a~B zK?P5G2o(L^`1s_;>Kp!Em5*w$-2hzM%GStz^lLO(zqc`N>`qB{V^ifuGOxppB*n3MV> zQ7}L56D4@e4Ns(Dnv$4yjE06iwEYESd#F~{v9X=8VMJ75IK_-F!Db${G%3nD+D`cr zny8%73PK>fy}kEZSs@q5b_G|i;GnylULf~zIEkY$lt~#&n&e+ZcN29kWF(oEjb=PL zJ##ST4nGlImywYv3D36LjlRdR)f^0saZ9M&Y$e&%WpJgsnWY@)($&+`Z`!X6_yCt`<^ zZf=j>7zg3E1KSDlN1kV+QseGp>8275c2qEG3kl<4+57GD9;S0+@DmMpf$m=b(6dyu z<^OE%Z869?EHmuyBF1n)fkGJ+Lo99CsHC8$AyM)9lcjuH4rR~VFuU)=0r)k^rY%jF zi2`8Hh7Tcp+V9U0hV*9h6GRx$v$-7sQ34xp1MhM=13-r(ey5`b@9R_x%)zh7i+=Ty zRe`la1rxL?n4nea1)oe1(*9A>r{FBNIW^FcomT;Zmu8muKD6+!t~>sY+iL$ z75RaIT*}SnA?XKrhXiA(Z+O|f_SNCI`O8)yb2IP$rG6#o8cbtg(uZBu6&Oq*P{XlW46ah9o746Q&{Xnfom{u>K_q_(_Qu;4@UMyF{7m?p8k0{~|| zrj@>^W%|*pQzcETGEtH$aP;%k%wAeSXAf#+caG6?F3>IS%i;j4x`M@H=# zgrcR0c_w-M4gd{eJXy6DiBb4urKx^^Ze6Jp>*RvZMxG$?{RIt@^sJf0*C`kPbfr_% zbHkhrhfS9kXpAxG#CmT*ep(~+GI%Xgp`E{{?85seoaH2!WCz?VCxh7bM)8c^o%IZB zNZo=sCBTbA5ObBCK2WB5Q6G!vk4Z-?7pZv98G)0&57YA+UzPp9g{FZaM6mn(;WV0j~p!tUO(P KUFw}9(*6aOn{tQ% diff --git a/dashboard/test/widgets/goldens/task_grid_test.withL.png b/dashboard/test/widgets/goldens/task_grid_test.withL.png deleted file mode 100644 index 88b151aaf8e3ee53894788437ee305cc88c0ee0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2296 zcmeAS@N?(olHy`uVBq!ia0y~yU{+vYV2a>i1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9jkefq`R@r;B4q#hkZy4YQ;ZC0H&@yvSO@*7!O`S)e0$Y4LOJC0^oJmY!n0 zw6i!>nrCN=f5HdJ8kXgOsVUx$yBAk{mFsAY&HR?#UjILo31~VD9QdCXw&S?#r1#c! z-_s}ES3U=nl(}$nAv;h(-|;;a>-#`fw80-iMWCt1_Y2;|hwH5g z0jjp%2r}Y|h5`eFLodn9Gv;$d%9=X;eKWY z2A`YTwkOnj7yW*8=edgfF-c(P%`oauV8jwsn{$PKt^fLb>1n|i1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9jkefq_%k)5S5QV$R#UhFP~0B-kGK9~D0#$uqaQw`t+GD{G_mFRY9{d2OZm zi)m{o#~OSQvUOBQKOuPMXo^8mQurn5=qfo5Cd;b7>bFm;|6RodG#>^YTrzEZ|05XF4$w)Z{hJphuEDQ`Cfet`v z307>(8Bfgi)aEBwzpP|nIKU_@0J905ez8tct72o^yInE+V{_S}r5PALSncTmD({%N zb&hp;QDP7y1H+sngsj8v2#6@qpzKGhfflPB1Q~-28q~u#Kfao@_fF}~y4|;8&fW(` zO>(NZ4g-V2vbVMh&;6u)#DHF$Jfi`q%aDtMfk9v~$;{2uihXNDfx(~xHU`=L4=J0h z<8-f`j-CE``|7hP|I2~i-~XnGm4V^Nmc8sx?woA!1S&5O0)?M~8zV3%l0-;lrd=yq z*SQ`TkuzAJ(U4Hnw&U)rWviaqrrN&g|Ma9+_5H@>It&e~uYBY22>-n5*VW)_$NvV- z+q3b>-?y6F3=gy_*|5g(b2+E~VWIQ;YRbYxxBt~=XJC-nJiYATub|NV{O4*{tSUdO z0}?ado&a7!sY1V%$(Gz3ONU^E24ArP~!e(O3{otF2q*Fk(wS3j3^P6i1B%QlYbpRzjKx9jP7LeL$-D$|Sc;uI zLpXq-h9jkefr0&#r;B4q#hkZy&vG6L5OBNLxkRj5FeXFrTLR~|1i@V#H@eSeFbgUx z-WM)9XS9FwULKWDpebT%U8SG{i943yx_jp7GVYux2)uCH4)PySr) z>Q{nuSQ!{H4zRH>FmwniFfcguFfcMOB(ZQXFbF6+FfcSoFfuVP98s}9{MvXo1H*%%#9GBzR5QlfTST40sA-v z7#I}X(3JYv7p>{`2Rd&}6ED!%Q7{?;qaiRF0;3@?8Un*01P+AfKVaqjdemqGu%2aL N@O1TaS?83{1OOOpWCQ>J diff --git a/dashboard/test/widgets/goldens/task_overlay_test.flaky_overlay_closed.png b/dashboard/test/widgets/goldens/task_overlay_test.flaky_overlay_closed.png deleted file mode 100644 index 074a979fe7ead79c6d90f6b4f6d2bdf48b77751d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25070 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 diff --git a/dashboard/test/widgets/goldens/task_overlay_test.flaky_overlay_open.png b/dashboard/test/widgets/goldens/task_overlay_test.flaky_overlay_open.png deleted file mode 100644 index 15a6cec45487cd361ad800b26b31e40a13d53a2b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28639 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 diff --git a/dashboard/test/widgets/goldens/task_overlay_test.nondevicelab_closed.png b/dashboard/test/widgets/goldens/task_overlay_test.nondevicelab_closed.png deleted file mode 100644 index 51c93f5645ac4a1328bec94e10eeef7b6809229c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25061 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 Bfr|hD diff --git a/dashboard/test/widgets/goldens/task_overlay_test.nondevicelab_open.png b/dashboard/test/widgets/goldens/task_overlay_test.nondevicelab_open.png deleted file mode 100644 index 5318313edf651d43a3d51a4aec38c2fb9ea43e1c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28444 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 XD5*xYD4Ns|_NT6*bvEOS`Mv)FUJ#XI diff --git a/dashboard/test/widgets/goldens/task_overlay_test.normal_overlay_closed.png b/dashboard/test/widgets/goldens/task_overlay_test.normal_overlay_closed.png deleted file mode 100644 index b8bb5f1767ed113434d24ee346e9acee7971e0de..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 26846 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 k2*HLnY-q!OpEm5txMD[ - LuciTaskAttemptSummary( - task: Task()..buildNumberList = '', - ), - ], - ), - ), - ); - - expect(find.byType(ElevatedButton), findsNothing); - }); - - testWidgets('shows only 1 button for 1 attempt', (WidgetTester tester) async { - await tester.pumpWidget( - MaterialApp( - home: Column( - children: [ - LuciTaskAttemptSummary( - task: Task()..buildNumberList = '123', - ), - ], - ), - ), - ); - - expect(find.byType(ElevatedButton), findsNWidgets(1)); - expect(find.text('OPEN LOG FOR BUILD #123'), findsOneWidget); - }); - - testWidgets('shows multiple buttons for multiple attempts', (WidgetTester tester) async { - await tester.pumpWidget( - MaterialApp( - home: Column( - children: [ - LuciTaskAttemptSummary( - task: Task()..buildNumberList = '123,456', - ), - ], - ), - ), - ); - - expect(find.byType(ElevatedButton), findsNWidgets(2)); - expect(find.text('OPEN LOG FOR BUILD #123'), findsOneWidget); - expect(find.text('OPEN LOG FOR BUILD #456'), findsOneWidget); - }); - - testWidgets('opens expected luci log url', (WidgetTester tester) async { - final FakeUrlLauncher urlLauncher = FakeUrlLauncher(); - UrlLauncherPlatform.instance = urlLauncher; - - await tester.pumpWidget( - MaterialApp( - home: Column( - children: [ - LuciTaskAttemptSummary( - task: Task() - ..key = (RootKey()..child = (Key()..name = 'loggylog')) - ..buildNumberList = '123' - ..builderName = 'Linux', - ), - ], - ), - ), - ); - - await tester.tap(find.byType(ElevatedButton)); - await tester.pump(); - - expect(urlLauncher.launches, isNotEmpty); - expect(urlLauncher.launches.single, 'https://ci.chromium.org/p/flutter/builders/prod/Linux/123'); - }); - - testWidgets('opens expected luci log url for when there are multiple tasks', (WidgetTester tester) async { - final FakeUrlLauncher urlLauncher = FakeUrlLauncher(); - UrlLauncherPlatform.instance = urlLauncher; - - await tester.pumpWidget( - MaterialApp( - home: Column( - children: [ - LuciTaskAttemptSummary( - task: Task() - ..key = (RootKey()..child = (Key()..name = 'loggylog')) - ..buildNumberList = '123,456' - ..builderName = 'Linux', - ), - ], - ), - ), - ); - - await tester.tap(find.text('OPEN LOG FOR BUILD #456')); - await tester.pump(); - - expect(urlLauncher.launches, isNotEmpty); - expect(urlLauncher.launches.single, '${LuciTaskAttemptSummary.luciProdLogBase}/prod/Linux/456'); - }); - - testWidgets('opens expected dart-internal log url', (WidgetTester tester) async { - final FakeUrlLauncher urlLauncher = FakeUrlLauncher(); - UrlLauncherPlatform.instance = urlLauncher; - - await tester.pumpWidget( - MaterialApp( - home: Column( - children: [ - LuciTaskAttemptSummary( - task: Task() - ..key = (RootKey()..child = (Key()..name = 'dart-internal-log')) - ..buildNumberList = '123' - ..builderName = 'Linux' - ..stageName = 'dart-internal', - ), - ], - ), - ), - ); - - await tester.tap(find.byType(ElevatedButton)); - await tester.pump(); - - expect(urlLauncher.launches, isNotEmpty); - expect(urlLauncher.launches.single, '${LuciTaskAttemptSummary.dartInternalLogBase}/flutter/Linux/123'); - }); - }); -} diff --git a/dashboard/test/widgets/task_grid_test.dart b/dashboard/test/widgets/task_grid_test.dart deleted file mode 100644 index 44ce0ce3d..000000000 --- a/dashboard/test/widgets/task_grid_test.dart +++ /dev/null @@ -1,882 +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:typed_data'; - -import 'package:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; -import 'package:flutter_app_icons/flutter_app_icons_platform_interface.dart'; -import 'package:flutter_dashboard/logic/qualified_task.dart'; -import 'package:flutter_dashboard/logic/task_grid_filter.dart'; -import 'package:flutter_dashboard/model/commit.pb.dart'; -import 'package:flutter_dashboard/model/commit_status.pb.dart'; -import 'package:flutter_dashboard/model/task.pb.dart'; -import 'package:flutter_dashboard/service/dev_cocoon.dart'; -import 'package:flutter_dashboard/state/build.dart'; -import 'package:flutter_dashboard/widgets/commit_box.dart'; -import 'package:flutter_dashboard/widgets/lattice.dart'; -import 'package:flutter_dashboard/widgets/state_provider.dart'; -import 'package:flutter_dashboard/widgets/task_box.dart'; -import 'package:flutter_dashboard/widgets/task_grid.dart'; -import 'package:flutter_dashboard/widgets/task_icon.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import '../utils/fake_build.dart'; -import '../utils/fake_flutter_app_icons.dart'; -import '../utils/golden.dart'; -import '../utils/mocks.dart'; -import '../utils/task_icons.dart'; - -const double _cellSize = 36; - -void main() { - setUp(() { - FlutterAppIconsPlatform.instance = FakeFlutterAppIcons(); - }); - - testWidgets('TaskGridContainer shows loading indicator when statuses is empty', (WidgetTester tester) async { - await tester.pumpWidget( - MaterialApp( - home: ValueProvider( - value: FakeBuildState(), - child: const Material( - child: TaskGridContainer(), - ), - ), - ), - ); - expect(find.byType(CircularProgressIndicator), findsOneWidget); - expect(find.byType(LatticeScrollView), findsNothing); - }); - - testWidgets('TaskGridContainer with DevelopmentCocoonService', (WidgetTester tester) async { - await precacheTaskIcons(tester); - final DevelopmentCocoonService service = DevelopmentCocoonService(DateTime.utc(2020)); - final BuildState buildState = BuildState( - cocoonService: service, - authService: MockGoogleSignInService(), - ); - void listener1() {} - buildState.addListener(listener1); - - await tester.pumpWidget( - TaskBox( - cellSize: _cellSize, - child: MaterialApp( - theme: ThemeData(useMaterial3: false), - home: ValueProvider( - value: buildState, - child: const Material( - child: TaskGridContainer(), - ), - ), - ), - ), - ); - await tester.pump(); - - final int commitCount = tester.elementList(find.byType(CommitBox)).length; - expect(commitCount, 16); // based on screen size this is how many show up - - final double xPosition = tester.getTopLeft(find.byType(CommitBox).first).dx; - - for (int index = 0; index < commitCount; index += 1) { - // All the x positions should match the first instance if they're all in the same column - expect(tester.getTopLeft(find.byType(CommitBox).at(index)).dx, xPosition); - } - - await expectGoldenMatches(find.byType(TaskGrid), 'task_grid_test.dev.origin.png'); - - // Check if the LOADING... indicator appears. - service.paused = true; - await tester.drag(find.byType(TaskGrid), const Offset(0.0, -5000.0)); - await tester.pumpAndSettle(); - await expectGoldenMatches(find.byType(TaskGrid), 'task_grid_test.dev.scroll_y.png'); - service.paused = false; - await tester.pumpAndSettle(); - - // Check the right edge after the data comes in. - service.paused = true; - await tester.drag(find.byType(TaskGrid), const Offset(-5000.0, 0.0)); - await tester.pumpAndSettle(); - await expectGoldenMatches(find.byType(TaskGrid), 'task_grid_test.dev.scroll_x.png'); - service.paused = false; - await tester.pumpAndSettle(); - - await tester.pumpWidget(Container()); - buildState.dispose(); - }); - - testWidgets('TaskGridContainer supports mouse drag', (WidgetTester tester) async { - await precacheTaskIcons(tester); - final DevelopmentCocoonService service = DevelopmentCocoonService(DateTime.utc(2020)); - final BuildState buildState = BuildState( - cocoonService: service, - authService: MockGoogleSignInService(), - ); - void listener1() {} - buildState.addListener(listener1); - - await tester.pumpWidget( - TaskBox( - cellSize: _cellSize, - child: MaterialApp( - theme: ThemeData(useMaterial3: false), - home: ValueProvider( - value: buildState, - child: const Material( - child: TaskGridContainer(), - ), - ), - ), - ), - ); - await tester.pump(); - - final int commitCount = tester.elementList(find.byType(CommitBox)).length; - expect(commitCount, 16); // based on screen size this is how many show up - - final double xPosition = tester.getTopLeft(find.byType(CommitBox).first).dx; - - for (int index = 0; index < commitCount; index += 1) { - // All the x positions should match the first instance if they're all in the same column - expect(tester.getTopLeft(find.byType(CommitBox).at(index)).dx, xPosition); - } - - await expectGoldenMatches(find.byType(TaskGrid), 'task_grid_test.dev.origin.png'); - - // Check if the LOADING... indicator appears. - service.paused = true; - - TestGesture gesture = await tester.startGesture( - tester.getCenter(find.byType(TaskGrid)), - kind: PointerDeviceKind.mouse, - ); - for (int i = 0; i < 100; i += 1) { - await gesture.moveBy(const Offset(0.0, -50.0)); - } - await gesture.up(); - - await tester.pumpAndSettle(); - await expectGoldenMatches(find.byType(TaskGrid), 'task_grid_test.dev.mouse_scroll_y.png'); - - await gesture.removePointer(); - service.paused = false; - await tester.pumpAndSettle(); - - // Check the right edge after the data comes in. - service.paused = true; - - gesture = await tester.startGesture( - tester.getCenter(find.byType(TaskGrid)), - kind: PointerDeviceKind.mouse, - ); - - for (int i = 0; i < 100; i += 1) { - await gesture.moveBy(const Offset(-50.0, 0)); - } - //await gesture.moveBy(const Offset(-5000, 0)); - await gesture.up(); - - await tester.pumpAndSettle(); - await expectGoldenMatches(find.byType(TaskGrid), 'task_grid_test.dev.mouse_scroll_x.png'); - - service.paused = false; - await gesture.removePointer(); - - await tester.pumpWidget(Container()); - buildState.dispose(); - }); - - testWidgets('TaskGridContainer with DevelopmentCocoonService - dark', (WidgetTester tester) async { - await precacheTaskIcons(tester); - final DevelopmentCocoonService service = DevelopmentCocoonService(DateTime.utc(2020)); - final BuildState buildState = BuildState( - cocoonService: service, - authService: MockGoogleSignInService(), - ); - void listener1() {} - buildState.addListener(listener1); - - await tester.pumpWidget( - TaskBox( - cellSize: _cellSize, - child: MaterialApp( - theme: ThemeData.dark(useMaterial3: false), - home: ValueProvider( - value: buildState, - child: const Material( - child: TaskGridContainer(), - ), - ), - ), - ), - ); - await tester.pump(); - - final int commitCount = tester.elementList(find.byType(CommitBox)).length; - expect(commitCount, 16); // based on screen size this is how many show up - - final double xPosition = tester.getTopLeft(find.byType(CommitBox).first).dx; - - for (int index = 0; index < commitCount; index += 1) { - // All the x positions should match the first instance if they're all in the same column - expect(tester.getTopLeft(find.byType(CommitBox).at(index)).dx, xPosition); - } - - await expectGoldenMatches(find.byType(TaskGrid), 'task_grid_test.dev.origin.dark.png'); - - // Check if the LOADING... indicator appears. - service.paused = true; - await tester.drag(find.byType(TaskGrid), const Offset(0.0, -5000.0)); - await tester.pumpAndSettle(); - await expectGoldenMatches(find.byType(TaskGrid), 'task_grid_test.dev.scroll_y.dark.png'); - service.paused = false; - await tester.pumpAndSettle(); - - // Check the right edge after the data comes in. - service.paused = true; - await tester.drag(find.byType(TaskGrid), const Offset(-5000.0, 0.0)); - await tester.pumpAndSettle(); - await expectGoldenMatches(find.byType(TaskGrid), 'task_grid_test.dev.scroll_x.dark.png'); - service.paused = false; - await tester.pumpAndSettle(); - - await tester.pumpWidget(Container()); - buildState.dispose(); - }); - - Future testGrid(WidgetTester tester, TaskGridFilter? filter, int rows, int cols) async { - final BuildState buildState = BuildState( - cocoonService: DevelopmentCocoonService(DateTime.utc(2020)), - authService: MockGoogleSignInService(), - ); - void listener1() {} - buildState.addListener(listener1); - - await tester.pumpWidget( - TaskBox( - cellSize: _cellSize, - child: MaterialApp( - theme: ThemeData.dark(), - home: ValueProvider( - value: buildState, - child: Material( - child: TaskGridContainer(filter: filter), - ), - ), - ), - ), - ); - await tester.pump(); - - expect(find.byType(LatticeScrollView), findsOneWidget); - final LatticeScrollView lattice = find.byType(LatticeScrollView).evaluate().first.widget as LatticeScrollView; - - expect(lattice.cells.length, rows); - for (final List row in lattice.cells) { - expect(row.length, cols); - } - - await tester.pumpWidget(Container()); - buildState.dispose(); - } - - testWidgets('Task name filter affects grid', (WidgetTester tester) async { - // Default filters - await testGrid(tester, null, 27, 101); - await testGrid(tester, TaskGridFilter(), 27, 101); - await testGrid(tester, TaskGridFilter.fromMap(null), 27, 101); - - // QualifiedTask (column) filters - await testGrid(tester, TaskGridFilter()..taskFilter = RegExp('Linux_android 2'), 27, 12); - - // CommitStatus (row) filters - await testGrid(tester, TaskGridFilter()..authorFilter = RegExp('bob'), 8, 101); - await testGrid(tester, TaskGridFilter()..messageFilter = RegExp('developer'), 18, 101); - await testGrid(tester, TaskGridFilter()..hashFilter = RegExp('2d22b5e85f986f3fa2cf1bfaf085905c2182c270'), 4, 101); - }); - - testWidgets('Skipped tasks do not break the grid', (WidgetTester tester) async { - await precacheTaskIcons(tester); - // Matrix Diagram: - // - // ✓☐☐ - // ☐✓☐ - // ☐☐✓ - // - // To construct the matrix from this diagram, each [CommitStatus] must have a unique [Task] - // that does not share its name with any other [Task]. This will make that [CommitStatus] have - // its task on its own unique row and column. - - final List statusesWithSkips = [ - CommitStatus() - ..commit = (Commit()..author = 'Author') - ..tasks.addAll( - [ - Task() - ..stageName = 'A' - ..name = '1' - ..builderName = '1' - ..status = TaskBox.statusSucceeded, - ], - ), - CommitStatus() - ..commit = (Commit()..author = 'Author') - ..tasks.addAll( - [ - Task() - ..stageName = 'A' - ..name = '2' - ..builderName = '2' - ..status = TaskBox.statusSucceeded, - ], - ), - CommitStatus() - ..commit = (Commit()..author = 'Author') - ..tasks.addAll( - [ - Task() - ..stageName = 'A' - ..name = '3' - ..builderName = '3' - ..status = TaskBox.statusSucceeded, - ], - ), - ]; - - await tester.pumpWidget( - MaterialApp( - theme: ThemeData(useMaterial3: false), - home: Material( - child: TaskGrid( - buildState: FakeBuildState(), - commitStatuses: statusesWithSkips, - ), - ), - ), - ); - - await expectGoldenMatches(find.byType(TaskGrid), 'task_grid_test.withSkips.png'); - }); - - testWidgets('Cocoon and LUCI tasks share the same column', (WidgetTester tester) async { - await precacheTaskIcons(tester); - // Matrix Diagram: - // - // ✓ - // ✓ - // - // To construct the matrix from this diagram, each [CommitStatus] will have a [Task] - // that shares its name, but will have a different stage name. - - final List statuses = [ - CommitStatus() - ..commit = (Commit()..author = 'Author') - ..tasks.addAll( - [ - Task() - ..stageName = StageName.cocoon - ..name = '1' - ..builderName = '1' - ..status = TaskBox.statusSucceeded, - ], - ), - CommitStatus() - ..commit = (Commit()..author = 'Author') - ..tasks.addAll( - [ - Task() - ..stageName = StageName.luci - ..name = '1' - ..builderName = '1' - ..status = TaskBox.statusSucceeded, - ], - ), - ]; - - await tester.pumpWidget( - MaterialApp( - home: Material( - child: TaskGrid( - buildState: FakeBuildState(), - commitStatuses: statuses, - ), - ), - ), - ); - - expect(find.byType(LatticeScrollView), findsOneWidget); - final LatticeScrollView lattice = find.byType(LatticeScrollView).evaluate().first.widget as LatticeScrollView; - - // Rows (task icon, two commits, load more row) - expect(lattice.cells.length, 4); - // Columns (commit box, task) - expect(lattice.cells.first.length, 2); - expect(lattice.cells[1].length, 2); - }); - - testWidgets('TaskGrid creates a task icon row and they line up', (WidgetTester tester) async { - final List commitStatuses = [ - CommitStatus() - ..commit = (Commit()..author = 'Author') - ..tasks.addAll( - [ - Task() - ..name = 'Task Name' - ..builderName = 'Task Name' - ..stageName = 'Stage Nome 1' - ..status = TaskBox.statusSucceeded, - Task() - ..name = 'Task Name' - ..builderName = 'Task Name' - ..stageName = 'Stage Nome 2' - ..status = TaskBox.statusFailed, - ], - ), - ]; - - await tester.pumpWidget( - MaterialApp( - home: Material( - child: TaskGrid( - buildState: FakeBuildState(), - commitStatuses: commitStatuses, - ), - ), - ), - ); - - expect(find.byType(TaskIcon), findsNWidgets(2)); - expect(tester.getTopLeft(find.byType(TaskIcon).at(0)).dy, tester.getTopLeft(find.byType(TaskIcon).at(1)).dy); - }); - - testWidgets('TaskGrid honors moreStatusesExist', (WidgetTester tester) async { - await precacheTaskIcons(tester); - final List commitStatuses = [ - CommitStatus() - ..commit = (Commit()..author = 'Author') - ..tasks.addAll( - [ - Task() - ..name = 'Task Name' - ..name = 'Task Name' - ..stageName = 'Stage Nome' - ..status = TaskBox.statusSucceeded, - ], - ), - ]; - - await tester.pumpWidget( - MaterialApp( - theme: ThemeData(useMaterial3: false), - home: Material( - child: TaskGrid( - buildState: FakeBuildState(moreStatusesExist: false), - commitStatuses: commitStatuses, - ), - ), - ), - ); - - await expectGoldenMatches(find.byType(TaskGrid), 'task_grid_test.withoutL.png'); - - await tester.pumpWidget( - MaterialApp( - theme: ThemeData(useMaterial3: false), - home: Material( - child: TaskGrid( - buildState: FakeBuildState(moreStatusesExist: true), - commitStatuses: commitStatuses, - ), - ), - ), - ); - - await expectGoldenMatches(find.byType(TaskGrid), 'task_grid_test.withL.png'); - }); - - testWidgets('TaskGrid shows icon for rerun tasks', (WidgetTester tester) async { - await tester.pumpWidget( - MaterialApp( - home: Material( - child: TaskGrid( - buildState: FakeBuildState( - authService: MockGoogleSignInService(), - cocoonService: MockCocoonService(), - ), - commitStatuses: [ - CommitStatus() - ..commit = (Commit()..author = 'Cast') - ..tasks.addAll( - [ - Task() - ..stageName = 'A' - ..status = 'Succeeded' - ..attempts = 2, - ], - ), - ], - ), - ), - ), - ); - expect(find.byIcon(Icons.priority_high), findsOneWidget); - await tester.pumpWidget( - MaterialApp( - home: Material( - child: TaskGrid( - buildState: FakeBuildState( - authService: MockGoogleSignInService(), - cocoonService: MockCocoonService(), - ), - commitStatuses: [ - CommitStatus() - ..commit = (Commit()..author = 'Cast') - ..tasks.addAll( - [ - Task() - ..stageName = 'A' - ..status = 'Succeeded' - ..attempts = 1, - ], - ), - ], - ), - ), - ), - ); - expect(find.byIcon(Icons.priority_high), findsNothing); - }); - - testWidgets('TaskGrid shows icon for isTestFlaky tasks', (WidgetTester tester) async { - await tester.pumpWidget( - MaterialApp( - home: Material( - child: TaskGrid( - buildState: FakeBuildState( - authService: MockGoogleSignInService(), - cocoonService: MockCocoonService(), - ), - commitStatuses: [ - CommitStatus() - ..commit = (Commit()..author = 'Cast') - ..tasks.addAll( - [ - Task() - ..stageName = 'A' - ..status = 'Succeeded' - ..attempts = 1 - ..isTestFlaky = true, - ], - ), - ], - ), - ), - ), - ); - expect(find.byIcon(Icons.priority_high), findsOneWidget); - await tester.pumpWidget( - MaterialApp( - home: Material( - child: TaskGrid( - buildState: FakeBuildState( - authService: MockGoogleSignInService(), - cocoonService: MockCocoonService(), - ), - commitStatuses: [ - CommitStatus() - ..commit = (Commit()..author = 'Cast') - ..tasks.addAll( - [ - Task() - ..stageName = 'A' - ..status = 'Succeeded' - ..attempts = 1 - ..isTestFlaky = false, - ], - ), - ], - ), - ), - ), - ); - expect(find.byIcon(Icons.priority_high), findsNothing); - }); - - testWidgets('TaskGrid shows icon for isTestFlaky tasks with multiple attempts', (WidgetTester tester) async { - final Task taskA3 = Task() - ..stageName = 'A' - ..builderName = '1' - ..name = 'A' - ..status = TaskBox.statusSucceeded - ..attempts = 3 - ..isTestFlaky = true; - - final Task taskB1 = Task() - ..stageName = 'B' - ..builderName = '2' - ..name = 'B' - ..status = TaskBox.statusSucceeded - ..attempts = 1 - ..isTestFlaky = false; - - await tester.pumpWidget( - MaterialApp( - home: Material( - child: TaskGrid( - buildState: FakeBuildState( - authService: MockGoogleSignInService(), - cocoonService: MockCocoonService(), - ), - commitStatuses: [ - CommitStatus() - ..commit = (Commit()..author = 'Cast') - ..tasks.addAll( - [ - taskA3, - ], - ), - CommitStatus() - ..commit = (Commit()..author = 'Cast') - ..tasks.addAll( - [taskB1], - ), - ], - ), - ), - ), - ); - expect(find.byIcon(Icons.priority_high), findsNWidgets(1)); - - // check the order of the items. The flaky should be to the left and first. - expect(find.byType(TaskGrid).first, findsAtLeastNWidgets(1)); - - final LatticeScrollView latticeScrollView = tester.firstWidget(find.byType(LatticeScrollView)); - final List> cells = latticeScrollView.cells; - final List myCells = cells.first; - expect(myCells.length, 3); - myCells.removeAt(0); // the first element is the github author box. - expect(myCells[0].taskName, 'A'); - expect(myCells[1].taskName, 'B'); - }); - - testWidgets('TaskGrid can handle all the various different statuses', (WidgetTester tester) async { - await precacheTaskIcons(tester); - final List statuses = [ - CommitStatus() - ..commit = (Commit()..author = 'Author') - ..tasks.addAll( - [ - Task() - ..stageName = 'A' - ..name = '1' - ..builderName = '1' - ..status = TaskBox.statusFailed, - Task() - ..stageName = 'A' - ..name = '2' - ..builderName = '2' - ..status = TaskBox.statusNew, - Task() - ..stageName = 'A' - ..name = '3' - ..builderName = '3' - ..status = TaskBox.statusSkipped, - Task() - ..stageName = 'A' - ..name = '4' - ..builderName = '4' - ..status = TaskBox.statusSucceeded, - Task() - ..stageName = 'A' - ..name = '5' - ..builderName = '5' - ..status = TaskBox.statusInProgress, - Task()..status = 'Invalid value', - ], - ), - CommitStatus() - ..commit = (Commit()..author = 'Author') - ..tasks.addAll( - [ - Task() - ..stageName = 'A' - ..name = '1' - ..builderName = '1' - ..attempts = 2 - ..status = TaskBox.statusFailed, - Task() - ..stageName = 'A' - ..name = '2' - ..builderName = '2' - ..attempts = 2 - ..status = TaskBox.statusNew, - Task() - ..stageName = 'A' - ..name = '3' - ..builderName = '3' - ..attempts = 2 - ..status = TaskBox.statusSkipped, - Task() - ..stageName = 'A' - ..name = '4' - ..builderName = '4' - ..attempts = 2 - ..status = TaskBox.statusSucceeded, - Task() - ..stageName = 'A' - ..name = '5' - ..builderName = '5' - ..attempts = 2 - ..status = TaskBox.statusInProgress, - Task()..status = 'Invalid value', - ], - ), - CommitStatus() - ..commit = (Commit()..author = 'Author') - ..tasks.addAll( - [ - Task() - ..stageName = 'A' - ..name = '1' - ..builderName = '1' - ..isFlaky = true - ..status = TaskBox.statusFailed, - Task() - ..stageName = 'A' - ..name = '2' - ..builderName = '2' - ..isFlaky = true - ..status = TaskBox.statusNew, - Task() - ..stageName = 'A' - ..name = '3' - ..builderName = '3' - ..isFlaky = true - ..status = TaskBox.statusSkipped, - Task() - ..stageName = 'A' - ..name = '4' - ..builderName = '4' - ..isFlaky = true - ..status = TaskBox.statusSucceeded, - Task() - ..stageName = 'A' - ..name = '5' - ..builderName = '5' - ..isFlaky = true - ..status = TaskBox.statusInProgress, - Task()..status = 'Invalid value', - ], - ), - CommitStatus() - ..commit = (Commit()..author = 'Author') - ..tasks.addAll( - [ - Task() - ..stageName = 'A' - ..name = '1' - ..builderName = '1' - ..attempts = 2 - ..isFlaky = true - ..status = TaskBox.statusFailed, - Task() - ..stageName = 'A' - ..name = '2' - ..builderName = '2' - ..attempts = 2 - ..isFlaky = true - ..status = TaskBox.statusNew, - Task() - ..stageName = 'A' - ..name = '3' - ..builderName = '3' - ..attempts = 2 - ..isFlaky = true - ..status = TaskBox.statusSkipped, - Task() - ..stageName = 'A' - ..name = '4' - ..builderName = '4' - ..attempts = 2 - ..isFlaky = true - ..status = TaskBox.statusSucceeded, - Task() - ..stageName = 'A' - ..name = '5' - ..builderName = '5' - ..attempts = 2 - ..isFlaky = true - ..status = TaskBox.statusInProgress, - Task()..status = 'Invalid value', - ], - ), - ]; - - await tester.pumpWidget( - MaterialApp( - theme: ThemeData(useMaterial3: false), - home: Material( - child: TaskGrid( - buildState: FakeBuildState(), - commitStatuses: statuses, - ), - ), - ), - ); - - await expectGoldenMatches(find.byType(TaskGrid), 'task_grid_test.differentTypes.png'); - }); - - // Table Driven Approach to ensure every message does show the corresponding color - TaskBox.statusColor.forEach((String message, Color color) { - testWidgets('Is the color $color when given the message $message', (WidgetTester tester) async { - await expectTaskBoxColorWithMessage(tester, message, color); - }); - }); -} - -Future expectTaskBoxColorWithMessage(WidgetTester tester, String message, Color expectedColor) async { - const double cellSize = 18; - const double cellPixelSize = cellSize * 3.0; - const double cellPixelArea = cellPixelSize * cellPixelSize; - await tester.pumpWidget( - MaterialApp( - home: Material( - child: Center( - child: SizedBox( - height: cellPixelSize, - width: cellPixelSize, - child: RepaintBoundary( - child: TaskGrid( - buildState: FakeBuildState( - authService: MockGoogleSignInService(), - cocoonService: MockCocoonService(), - ), - commitStatuses: [ - CommitStatus() - ..commit = (Commit()..author = 'Mathilda') - ..tasks.addAll( - [Task()..status = message], - ), - ], - ), - ), - ), - ), - ), - ), - ); - final RenderRepaintBoundary? renderObject = - tester.renderObject(find.byType(TaskGrid)).parent as RenderRepaintBoundary?; - final ByteData? pixels = await tester.runAsync(() async { - return (await renderObject!.toImage()).toByteData(); - }); - expect(pixels!.lengthInBytes, (cellPixelArea * 4).round()); - const double padding = 4.0; - final int rgba = pixels.getUint32((((cellPixelSize * (cellSize + padding)) + cellSize + padding).ceil()) * 4); - expect((rgba >> 8) | (rgba << 24) & 0xFFFFFFFF, expectedColor.value); -} diff --git a/dashboard/test/widgets/task_icon_test.dart b/dashboard/test/widgets/task_icon_test.dart deleted file mode 100644 index 019a634ec..000000000 --- a/dashboard/test/widgets/task_icon_test.dart +++ /dev/null @@ -1,230 +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:flutter/gestures.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_dashboard/logic/qualified_task.dart'; -import 'package:flutter_dashboard/widgets/task_icon.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; - -import '../utils/fake_url_launcher.dart'; - -void main() { - testWidgets('TaskIcon tooltip shows task name', (WidgetTester tester) async { - const String stageName = 'stagey stage'; - const String taskName = 'tasky task'; - const String expectedLabel = 'tasky task (stagey stage)'; - - await tester.pumpWidget( - const MaterialApp( - home: Material( - child: TaskIcon( - qualifiedTask: QualifiedTask(stage: stageName, task: taskName), - ), - ), - ), - ); - - expect(find.text(expectedLabel), findsNothing); - - final Finder taskIcon = find.byType(TaskIcon); - final TestGesture gesture = await tester.startGesture(tester.getCenter(taskIcon)); - await tester.pump(kLongPressTimeout); - - expect(find.text(expectedLabel), findsOneWidget); - - await gesture.up(); - }); - - testWidgets('Tapping TaskIcon opens source configuration url', (WidgetTester tester) async { - final FakeUrlLauncher urlLauncher = FakeUrlLauncher(); - UrlLauncherPlatform.instance = urlLauncher; - - const QualifiedTask luciTask = QualifiedTask(stage: StageName.luci, task: 'test'); - - await tester.pumpWidget( - const MaterialApp( - home: Material( - child: TaskIcon( - qualifiedTask: luciTask, - ), - ), - ), - ); - - // Tap to open the source configuration - await tester.tap(find.byType(TaskIcon)); - await tester.pump(); - - expect(urlLauncher.launches, isNotEmpty); - expect(urlLauncher.launches.single, luciTask.sourceConfigurationUrl); - }); - - testWidgets('Unknown stage name shows helper icon in TaskIcon', (WidgetTester tester) async { - await tester.pumpWidget( - const MaterialApp( - home: Material( - child: TaskIcon( - qualifiedTask: QualifiedTask(stage: 'stage not to be named', task: 'macbeth'), - ), - ), - ), - ); - - expect(find.byIcon(Icons.help), findsOneWidget); - }); - - testWidgets('TaskIcon shows the right icon for google test', (WidgetTester tester) async { - await tester.pumpWidget( - const MaterialApp( - home: Material( - child: TaskIcon( - qualifiedTask: QualifiedTask(stage: 'google_internal'), - ), - ), - ), - ); - - expect((tester.widget(find.byType(Image)) as Image).image, isInstanceOf()); - expect(((tester.widget(find.byType(Image)) as Image).image as AssetImage).assetName, 'assets/googleLogo.png'); - }); - - testWidgets('TaskIcon shows the right icon for web', (WidgetTester tester) async { - await tester.pumpWidget( - const MaterialApp( - home: Material( - child: TaskIcon( - qualifiedTask: QualifiedTask(stage: 'chromebot', task: 'Windows_web test', pool: 'luci.flutter.prod'), - ), - ), - ), - ); - - expect((tester.widget(find.byType(Image)) as Image).image, isInstanceOf()); - expect(((tester.widget(find.byType(Image)) as Image).image as AssetImage).assetName, 'assets/chromium.png'); - }); - - testWidgets('TaskIcon shows the right icon for LUCI windows', (WidgetTester tester) async { - await tester.pumpWidget( - const MaterialApp( - home: Material( - child: TaskIcon( - qualifiedTask: QualifiedTask(stage: 'chromebot', task: 'Windows something', pool: 'luci.flutter.prod'), - ), - ), - ), - ); - - expect((tester.widget(find.byType(Image)) as Image).image, isInstanceOf()); - expect(((tester.widget(find.byType(Image)) as Image).image as AssetImage).assetName, 'assets/windows.png'); - }); - - testWidgets('TaskIcon shows the right icon for fuchsia', (WidgetTester tester) async { - await tester.pumpWidget( - const MaterialApp( - home: Material( - child: TaskIcon( - qualifiedTask: - QualifiedTask(stage: 'chromebot', task: 'Windows_fuchsia something', pool: 'luci.flutter.prod'), - ), - ), - ), - ); - - expect((tester.widget(find.byType(Image)) as Image).image, isInstanceOf()); - expect(((tester.widget(find.byType(Image)) as Image).image as AssetImage).assetName, 'assets/fuchsia.png'); - }); - - testWidgets('TaskIcon shows the right icon for LUCI android', (WidgetTester tester) async { - await tester.pumpWidget( - const MaterialApp( - home: Material( - child: TaskIcon( - qualifiedTask: QualifiedTask(stage: 'chromebot', task: 'Windows_android test', pool: 'luci.flutter.prod'), - ), - ), - ), - ); - - expect(tester.widget(find.byType(Icon)) as Icon, isInstanceOf()); - expect((tester.widget(find.byType(Icon)) as Icon).icon!.codePoint, const Icon(Icons.android).icon!.codePoint); - }); - - testWidgets('TaskIcon shows the right icon for LUCI mac', (WidgetTester tester) async { - await tester.pumpWidget( - const MaterialApp( - home: Material( - child: TaskIcon( - qualifiedTask: QualifiedTask(stage: 'chromebot', task: 'Mac test', pool: 'luci.flutter.prod'), - ), - ), - ), - ); - - expect((tester.widget(find.byType(Image)) as Image).image, isInstanceOf()); - expect(((tester.widget(find.byType(Image)) as Image).image as AssetImage).assetName, 'assets/apple.png'); - }); - - testWidgets('TaskIcon shows the right icon for LUCI mac/iphone', (WidgetTester tester) async { - await tester.pumpWidget( - const MaterialApp( - home: Material( - child: TaskIcon( - qualifiedTask: QualifiedTask(stage: 'chromebot', task: 'Mac_ios test', pool: 'luci.flutter.prod'), - ), - ), - ), - ); - - expect(tester.widget(find.byType(Icon)) as Icon, isInstanceOf()); - expect((tester.widget(find.byType(Icon)) as Icon).icon!.codePoint, const Icon(Icons.phone_iphone).icon!.codePoint); - }); - - testWidgets('TaskIcon shows the right icon for LUCI linux', (WidgetTester tester) async { - await tester.pumpWidget( - const MaterialApp( - home: Material( - child: TaskIcon( - qualifiedTask: QualifiedTask(stage: 'chromebot', task: 'Linux test', pool: 'luci.flutter.prod'), - ), - ), - ), - ); - - expect((tester.widget(find.byType(Image)) as Image).image, isInstanceOf()); - expect(((tester.widget(find.byType(Image)) as Image).image as AssetImage).assetName, 'assets/linux.png'); - }); - - testWidgets('TaskIcon shows the right icon for unknown', (WidgetTester tester) async { - await tester.pumpWidget( - const MaterialApp( - home: Material( - child: TaskIcon( - qualifiedTask: QualifiedTask(stage: 'chromebot', task: 'Unknown', pool: 'luci.flutter.prod'), - ), - ), - ), - ); - - expect(tester.widget(find.byType(Icon)) as Icon, isInstanceOf()); - expect((tester.widget(find.byType(Icon)) as Icon).icon!.codePoint, const Icon(Icons.help).icon!.codePoint); - }); - - testWidgets('TaskIcon shows the right icon for dart-internal linux', (WidgetTester tester) async { - await tester.pumpWidget( - const MaterialApp( - home: Material( - child: TaskIcon( - qualifiedTask: - QualifiedTask(stage: 'dart-internal', task: 'Linux dart-internal test', pool: 'luci.flutter.prod'), - ), - ), - ), - ); - - expect((tester.widget(find.byType(Image)) as Image).image, isInstanceOf()); - expect(((tester.widget(find.byType(Image)) as Image).image as AssetImage).assetName, 'assets/linux.png'); - }); -} diff --git a/dashboard/test/widgets/task_overlay_test.dart b/dashboard/test/widgets/task_overlay_test.dart deleted file mode 100644 index ba8f8a0c9..000000000 --- a/dashboard/test/widgets/task_overlay_test.dart +++ /dev/null @@ -1,606 +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:flutter/material.dart'; -import 'package:flutter_dashboard/logic/qualified_task.dart'; -import 'package:flutter_dashboard/model/commit.pb.dart'; -import 'package:flutter_dashboard/model/commit_status.pb.dart'; -import 'package:flutter_dashboard/model/task.pb.dart'; -import 'package:flutter_dashboard/state/build.dart'; -import 'package:flutter_dashboard/widgets/error_brook_watcher.dart'; -import 'package:flutter_dashboard/widgets/luci_task_attempt_summary.dart'; -import 'package:flutter_dashboard/widgets/now.dart'; -import 'package:flutter_dashboard/widgets/progress_button.dart'; -import 'package:flutter_dashboard/widgets/state_provider.dart'; -import 'package:flutter_dashboard/widgets/task_box.dart'; -import 'package:flutter_dashboard/widgets/task_grid.dart'; -import 'package:flutter_dashboard/widgets/task_overlay.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; - -import '../utils/fake_build.dart'; -import '../utils/golden.dart'; -import '../utils/task_icons.dart'; - -class TestGrid extends StatelessWidget { - const TestGrid({ - required this.buildState, - required this.task, - super.key, - }); - - final BuildState buildState; - final Task task; - - @override - Widget build(BuildContext context) { - return Material( - child: TaskGrid( - buildState: buildState, - commitStatuses: [ - CommitStatus() - ..commit = (Commit() - ..author = 'Fats Domino' - ..sha = '24e8c0a2') - ..tasks.addAll([task]), - ], - ), - ); - } -} - -const double _cellSize = 36; - -void main() { - final DateTime nowTime = DateTime.utc(2020, 9, 1, 12, 30); - final DateTime createTime = nowTime.subtract(const Duration(minutes: 52)); - final DateTime startTime = nowTime.subtract(const Duration(minutes: 50)); - final DateTime finishTime = nowTime.subtract(const Duration(minutes: 10)); - - Int64 int64FromDateTime(DateTime time) => Int64(time.millisecondsSinceEpoch); - - late FakeBuildState buildState; - - setUp(() { - buildState = FakeBuildState(); - when(buildState.authService.isAuthenticated).thenReturn(true); - }); - - testWidgets('TaskOverlay shows on click', (WidgetTester tester) async { - await precacheTaskIcons(tester); - - final Task expectedTask = Task() - ..attempts = 3 - ..stageName = StageName.luci - ..name = 'Tasky McTaskFace' - ..reservedForAgentId = 'Agenty McAgentFace' - ..isFlaky = false // As opposed to the next test. - ..status = TaskBox.statusFailed - ..createTimestamp = int64FromDateTime(createTime) - ..startTimestamp = int64FromDateTime(startTime) - ..endTimestamp = int64FromDateTime(finishTime); - - final String expectedTaskInfoString = 'Attempts: ${expectedTask.attempts}\n' - 'Run time: 40 minutes\n' - 'Queue time: 2 minutes'; - - await tester.pumpWidget( - Now.fixed( - dateTime: nowTime, - child: TaskBox( - cellSize: _cellSize, - child: MaterialApp( - theme: ThemeData(useMaterial3: false), - home: Scaffold( - body: TestGrid( - buildState: buildState, - task: expectedTask, - ), - ), - ), - ), - ), - ); - await tester.pump(); - - expect(find.text(expectedTask.name), findsNothing); - expect(find.text(expectedTaskInfoString), findsNothing); - expect(find.text(expectedTask.reservedForAgentId), findsNothing); - - await expectGoldenMatches(find.byType(MaterialApp), 'task_overlay_test.normal_overlay_closed.png'); - - await tester.tapAt(const Offset(_cellSize * 1.5, _cellSize * 1.5)); - await tester.pump(); - - expect(find.text(expectedTask.name), findsOneWidget); - expect(find.text(expectedTaskInfoString), findsOneWidget); - - await expectGoldenMatches(find.byType(MaterialApp), 'task_overlay_test.normal_overlay_open.png'); - - // Since the overlay positions itself below the middle of the widget, - // it is safe to click the widget to close it again. - await tester.tapAt(const Offset(_cellSize * 1.5, _cellSize * 1.5)); - await tester.pump(); - - expect(find.text(expectedTask.name), findsNothing); - expect(find.text(expectedTaskInfoString), findsNothing); - expect(find.text(expectedTask.reservedForAgentId), findsNothing); - - await expectGoldenMatches(find.byType(MaterialApp), 'task_overlay_test.normal_overlay_closed.png'); - }); - - testWidgets('TaskOverlay shows when flaky is true', (WidgetTester tester) async { - await precacheTaskIcons(tester); - final Task flakyTask = Task() - ..attempts = 3 - ..stageName = StageName.luci - ..name = 'Tasky McTaskFace' - ..isFlaky = true // This is the point of this test. - ..status = TaskBox.statusFailed - ..createTimestamp = int64FromDateTime(createTime) - ..startTimestamp = int64FromDateTime(startTime) - ..endTimestamp = int64FromDateTime(finishTime); - - final String flakyTaskInfoString = 'Attempts: ${flakyTask.attempts}\n' - 'Run time: 40 minutes\n' - 'Queue time: 2 minutes\n' - 'Flaky: true'; - - await tester.pumpWidget( - Now.fixed( - dateTime: nowTime, - child: TaskBox( - cellSize: _cellSize, - child: MaterialApp( - theme: ThemeData(useMaterial3: false), - home: Scaffold( - body: TestGrid( - buildState: buildState, - task: flakyTask, - ), - ), - ), - ), - ), - ); - await tester.pump(); - - expect(find.text(flakyTask.name), findsNothing); - expect(find.text(flakyTaskInfoString), findsNothing); - expect(find.text(flakyTask.reservedForAgentId), findsNothing); - - await expectGoldenMatches(find.byType(MaterialApp), 'task_overlay_test.flaky_overlay_closed.png'); - - await tester.tapAt(const Offset(_cellSize * 1.5, _cellSize * 1.5)); - await tester.pump(); - - expect(find.text(flakyTask.name), findsOneWidget); - expect(find.text(flakyTaskInfoString), findsOneWidget); - - await expectGoldenMatches(find.byType(MaterialApp), 'task_overlay_test.flaky_overlay_open.png'); - }); - - testWidgets('TaskOverlay computes durations correctly for completed task', (WidgetTester tester) async { - /// Create a queue time of 2 minutes, run time of 8 minutes - final DateTime createTime = nowTime.subtract(const Duration(minutes: 11)); - final DateTime startTime = nowTime.subtract(const Duration(minutes: 9)); - final DateTime finishTime = nowTime.subtract(const Duration(minutes: 1)); - - final Task timeTask = Task() - ..attempts = 1 - ..stageName = StageName.luci - ..name = 'Tasky McTaskFace' - ..isFlaky = false - ..createTimestamp = int64FromDateTime(createTime) - ..startTimestamp = int64FromDateTime(startTime) - ..endTimestamp = int64FromDateTime(finishTime); - - final String timeTaskInfoString = 'Attempts: ${timeTask.attempts}\n' - 'Run time: 8 minutes\n' - 'Queue time: 2 minutes'; - - await tester.pumpWidget( - Now.fixed( - dateTime: nowTime, - child: TaskBox( - cellSize: _cellSize, - child: MaterialApp( - home: Scaffold( - body: TestGrid( - buildState: buildState, - task: timeTask, - ), - ), - ), - ), - ), - ); - - expect(find.text(timeTaskInfoString), findsNothing); - - // open the overlay to show the task summary - await tester.tapAt(const Offset(_cellSize * 1.5, _cellSize * 1.5)); - await tester.pump(); - - expect(find.text(timeTaskInfoString), findsOneWidget); - }); - - testWidgets('TaskOverlay computes durations correctly for running task', (WidgetTester tester) async { - /// Create a queue time of 2 minutes, running time of 9 minutes - final DateTime createTime = nowTime.subtract(const Duration(minutes: 11)); - final DateTime startTime = nowTime.subtract(const Duration(minutes: 9)); - - final Task timeTask = Task() - ..attempts = 1 - ..stageName = StageName.luci - ..name = 'Tasky McTaskFace' - ..status = TaskBox.statusInProgress - ..isFlaky = false - ..createTimestamp = int64FromDateTime(createTime) - ..startTimestamp = int64FromDateTime(startTime); - - final String timeTaskInfoString = 'Attempts: ${timeTask.attempts}\n' - 'Running for 9 minutes\n' - 'Queue time: 2 minutes'; - - await tester.pumpWidget( - Now.fixed( - dateTime: nowTime, - child: TaskBox( - cellSize: _cellSize, - child: MaterialApp( - home: Scaffold( - body: TestGrid( - buildState: buildState, - task: timeTask, - ), - ), - ), - ), - ), - ); - - expect(find.text(timeTaskInfoString), findsNothing); - - // open the overlay to show the task summary - await tester.tapAt(const Offset(_cellSize * 1.5, _cellSize * 1.5)); - await tester.pump(); - - expect(find.text(timeTaskInfoString), findsOneWidget); - }); - - testWidgets('TaskOverlay computes durations correctly for queueing task', (WidgetTester tester) async { - /// Create a queue time of 2 minutes - final DateTime createTime = nowTime.subtract(const Duration(minutes: 2)); - - final Task timeTask = Task() - ..attempts = 1 - ..stageName = StageName.luci - ..name = 'Tasky McTaskFace' - ..status = TaskBox.statusNew - ..isFlaky = false - ..createTimestamp = int64FromDateTime(createTime); - - final String timeTaskInfoString = 'Attempts: ${timeTask.attempts}\n' - 'Queueing for 2 minutes'; - - await tester.pumpWidget( - Now.fixed( - dateTime: nowTime, - child: TaskBox( - cellSize: _cellSize, - child: MaterialApp( - home: Scaffold( - body: TestGrid( - buildState: buildState, - task: timeTask, - ), - ), - ), - ), - ), - ); - - expect(find.text(timeTaskInfoString), findsNothing); - - // open the overlay to show the task summary - await tester.tapAt(const Offset(_cellSize * 1.5, _cellSize * 1.5)); - await tester.pump(); - - expect(find.text(timeTaskInfoString), findsOneWidget); - }); - - testWidgets('TaskOverlay shows the right message for nondevicelab tasks', (WidgetTester tester) async { - await precacheTaskIcons(tester); - await tester.pumpWidget( - Now.fixed( - dateTime: nowTime, - child: TaskBox( - cellSize: _cellSize, - child: MaterialApp( - theme: ThemeData(useMaterial3: false), - home: Scaffold( - body: TestGrid( - buildState: buildState, - task: Task() - ..stageName = 'luci' - ..status = TaskBox.statusSucceeded, - ), - ), - ), - ), - ), - ); - await tester.pump(); - - await expectGoldenMatches(find.byType(MaterialApp), 'task_overlay_test.nondevicelab_closed.png'); - - await tester.tapAt(const Offset(_cellSize * 1.5, _cellSize * 1.5)); - await tester.pump(); - - await expectGoldenMatches(find.byType(MaterialApp), 'task_overlay_test.nondevicelab_open.png'); - }); - - testWidgets('TaskOverlay shows TaskAttemptSummary for Luci tasks', (WidgetTester tester) async { - await tester.pumpWidget( - Now.fixed( - dateTime: nowTime, - child: TaskBox( - cellSize: _cellSize, - child: MaterialApp( - home: Scaffold( - body: TestGrid( - buildState: buildState, - task: Task() - ..stageName = 'chromebot' - ..status = TaskBox.statusSucceeded - ..buildNumberList = '123', - ), - ), - ), - ), - ), - ); - - expect(find.byType(LuciTaskAttemptSummary), findsNothing); - - await tester.tapAt(const Offset(_cellSize * 1.5, _cellSize * 1.5)); - await tester.pump(); - - expect(find.byType(LuciTaskAttemptSummary), findsOneWidget); - }); - - testWidgets('TaskOverlay shows TaskAttemptSummary for dart-internal tasks', (WidgetTester tester) async { - await tester.pumpWidget( - Now.fixed( - dateTime: nowTime, - child: TaskBox( - cellSize: _cellSize, - child: MaterialApp( - home: Scaffold( - body: TestGrid( - buildState: buildState, - task: Task() - ..stageName = 'dart-internal' - ..status = TaskBox.statusSucceeded - ..buildNumberList = '123', - ), - ), - ), - ), - ), - ); - - expect(find.byType(LuciTaskAttemptSummary), findsNothing); - - await tester.tapAt(const Offset(_cellSize * 1.5, _cellSize * 1.5)); - await tester.pump(); - - expect(find.byType(LuciTaskAttemptSummary), findsOneWidget); - }); - - testWidgets('TaskOverlay: RERUN button disabled when user !isAuthenticated', (WidgetTester tester) async { - final Task expectedTask = Task() - ..attempts = 3 - ..stageName = StageName.luci - ..name = 'Tasky McTaskFace' - ..reservedForAgentId = 'Agenty McAgentFace' - ..isFlaky = false; - - final FakeBuildState buildState = FakeBuildState(rerunTaskResult: true); - when(buildState.authService.isAuthenticated).thenReturn(false); - - await tester.pumpWidget( - Now.fixed( - dateTime: nowTime, - child: TaskBox( - cellSize: _cellSize, - child: MaterialApp( - home: Scaffold( - body: TestGrid( - buildState: buildState, - task: expectedTask, - ), - ), - ), - ), - ), - ); - - // Open the overlay - await tester.tapAt(const Offset(_cellSize * 1.5, _cellSize * 1.5)); - await tester.pump(); - - final ProgressButton? rerun = tester.element(find.text('RERUN')).findAncestorWidgetOfExactType(); - - expect(rerun, isNotNull, reason: 'The rerun button should exist.'); - expect(rerun!.onPressed, isNull, reason: 'The rerun button should be disabled.'); - }); - - testWidgets('TaskOverlay: successful rerun shows success snackbar message', (WidgetTester tester) async { - final Task expectedTask = Task() - ..attempts = 3 - ..stageName = StageName.luci - ..name = 'Tasky McTaskFace' - ..reservedForAgentId = 'Agenty McAgentFace' - ..isFlaky = false; - - final FakeBuildState buildState = FakeBuildState(rerunTaskResult: true); - when(buildState.authService.isAuthenticated).thenReturn(true); - - await tester.pumpWidget( - Now.fixed( - dateTime: nowTime, - child: TaskBox( - cellSize: _cellSize, - child: MaterialApp( - home: Scaffold( - body: TestGrid( - buildState: buildState, - task: expectedTask, - ), - ), - ), - ), - ), - ); - - // Open the overlay - await tester.tapAt(const Offset(_cellSize * 1.5, _cellSize * 1.5)); - await tester.pump(); - - expect(find.text(TaskOverlayContents.rerunErrorMessage), findsNothing); - expect(find.text(TaskOverlayContents.rerunSuccessMessage), findsNothing); - - // Click the rerun task button - await tester.tap(find.text('RERUN')); - await tester.pump(); - await tester.pump(const Duration(milliseconds: 750)); // open animation - - expect(find.text(TaskOverlayContents.rerunErrorMessage), findsNothing); - expect(find.text(TaskOverlayContents.rerunSuccessMessage), findsOneWidget); - - // Snackbar message should go away after its duration - await tester.pump(TaskOverlayContents.rerunSnackBarDuration); - await tester.pump(const Duration(milliseconds: 1500)); // close animation - - expect(find.text(TaskOverlayContents.rerunErrorMessage), findsNothing); - expect(find.text(TaskOverlayContents.rerunSuccessMessage), findsNothing); - }); - - testWidgets('failed rerun shows errorBrook snackbar message', (WidgetTester tester) async { - final Task expectedTask = Task() - ..attempts = 3 - ..stageName = StageName.luci - ..name = 'Tasky McTaskFace' - ..reservedForAgentId = 'Agenty McAgentFace' - ..isFlaky = false - ..status = TaskBox.statusNew; - - final FakeBuildState buildState = FakeBuildState(rerunTaskResult: false); - when(buildState.authService.isAuthenticated).thenReturn(true); - - await tester.pumpWidget( - Now.fixed( - dateTime: nowTime, - child: TaskBox( - cellSize: _cellSize, - child: MaterialApp( - home: ValueProvider( - value: buildState, - child: Scaffold( - body: ErrorBrookWatcher( - errors: buildState.errors, - child: TestGrid( - buildState: buildState, - task: expectedTask, - ), - ), - ), - ), - ), - ), - ), - ); - - await tester.tapAt(const Offset(_cellSize * 1.5, _cellSize * 1.5)); - // await tester.tap(find.byType(LatticeCell)); - // await tester.tap(find.byType(TaskOverlayContents)); - await tester.pump(); - - // Click the rerun task button - await tester.tap(find.text('RERUN')); - await tester.pump(); - await tester.pump(const Duration(milliseconds: 750)); // open animation - - expect(find.text(TaskOverlayContents.rerunErrorMessage), findsOneWidget); - expect(find.text(TaskOverlayContents.rerunSuccessMessage), findsNothing); - - // Snackbar message should go away after its duration - await tester.pump(ErrorBrookWatcher.errorSnackbarDuration); // wait the duration - await tester.pump(); // schedule animation - await tester.pump(const Duration(milliseconds: 1500)); // close animation - - expect(find.text(TaskOverlayContents.rerunErrorMessage), findsNothing); - expect(find.text(TaskOverlayContents.rerunSuccessMessage), findsNothing); - }); - - test('TaskOverlayEntryPositionDelegate.positionDependentBox', () async { - const Size normalSize = Size(800, 600); - const Size childSize = Size(300, 180); - - // Window is too small, center. - expect( - TaskOverlayEntryPositionDelegate.positionDependentBox( - size: const Size(250, 150), - childSize: childSize, - cellSize: _cellSize, - target: const Offset(50.0, 50.0), - ), - const Offset(-25.0, 10.0), - ); - - // Normal positioning, below and to right. - expect( - TaskOverlayEntryPositionDelegate.positionDependentBox( - size: normalSize, - childSize: childSize, - cellSize: _cellSize, - target: const Offset(50.0, 50.0), - ), - const Offset(50.0, 82.4), - ); - // Doesn't fit in right, below and to left. - expect( - TaskOverlayEntryPositionDelegate.positionDependentBox( - size: normalSize, - childSize: childSize, - cellSize: _cellSize, - target: const Offset(590.0, 50.0), - ), - const Offset(490.0, 82.4), - ); - // Doesn't fit below, above and to right. - expect( - TaskOverlayEntryPositionDelegate.positionDependentBox( - size: normalSize, - childSize: childSize, - cellSize: _cellSize, - target: const Offset(50.0, 500.0), - ), - const Offset(50.0, 320.0), - ); - // Above and to left. - expect( - TaskOverlayEntryPositionDelegate.positionDependentBox( - size: normalSize, - childSize: childSize, - cellSize: _cellSize, - target: const Offset(590.0, 500.0), - ), - const Offset(490.0, 320.0), - ); - }); -} diff --git a/dashboard/test/widgets/user_sign_in_test.dart b/dashboard/test/widgets/user_sign_in_test.dart deleted file mode 100644 index 97f11f415..000000000 --- a/dashboard/test/widgets/user_sign_in_test.dart +++ /dev/null @@ -1,122 +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:flutter/material.dart'; -import 'package:flutter_dashboard/service/google_authentication.dart'; -import 'package:flutter_dashboard/widgets/user_sign_in.dart'; -import 'package:flutter_dashboard/widgets/state_provider.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:google_sign_in/google_sign_in.dart'; -import 'package:mockito/mockito.dart'; - -import '../utils/fake_google_account.dart'; -import '../utils/golden.dart'; -import '../utils/mocks.dart'; - -final Widget testApp = MaterialApp( - theme: ThemeData(useMaterial3: false), - home: Scaffold( - appBar: AppBar( - actions: const [ - UserSignIn(), - ], - ), - ), -); - -void main() { - late GoogleSignInService mockAuthService; - - setUp(() { - mockAuthService = MockGoogleSignInService(); - }); - - tearDown(() { - clearInteractions(mockAuthService); - }); - - testWidgets('SignInButton shows sign in when not authenticated', (WidgetTester tester) async { - when(mockAuthService.isAuthenticated).thenReturn(false); - when(mockAuthService.user).thenReturn(null); - - await tester.pumpWidget( - ValueProvider( - value: mockAuthService, - child: testApp, - ), - ); - await tester.pump(); - - expect(find.byType(GoogleUserCircleAvatar), findsNothing); - expect(find.text('SIGN IN'), findsOneWidget); - expect(find.text('test@flutter.dev'), findsNothing); - await expectGoldenMatches(find.byType(Overlay), 'sign_in_button.not_authenticated.png'); - }); - - testWidgets('SignInButton calls sign in on tap when not authenticated', (WidgetTester tester) async { - when(mockAuthService.isAuthenticated).thenReturn(false); - when(mockAuthService.user).thenReturn(null); - - await tester.pumpWidget( - ValueProvider( - value: mockAuthService, - child: testApp, - ), - ); - await tester.pump(); - - verifyNever(mockAuthService.signIn()); - - await tester.tap(find.text('SIGN IN')); - await tester.pump(); - - verify(mockAuthService.signIn()).called(1); - }); - - testWidgets('SignInButton shows avatar when authenticated', (WidgetTester tester) async { - when(mockAuthService.isAuthenticated).thenReturn(true); - - final GoogleSignInAccount user = FakeGoogleSignInAccount(); - when(mockAuthService.user).thenReturn(user); - - await tester.pumpWidget( - ValueProvider( - value: mockAuthService, - child: testApp, - ), - ); - await tester.pump(); - - // TODO(chillers): look for GoogleUserCircleAvatar once we use that (see sign_in_button.dart) - expect(find.text('SIGN IN'), findsNothing); - expect(find.text('test@flutter.dev'), findsOneWidget); - // TODO(xu-baolin): Re-enable this ASAP. - // Tracking at https://github.com/flutter/flutter/issues/73527 - // await expectGoldenMatches(find.byType(Overlay), 'sign_in_button.authenticated.png'); - }); - - testWidgets('SignInButton calls sign out on tap when authenticated', (WidgetTester tester) async { - when(mockAuthService.isAuthenticated).thenReturn(true); - - final GoogleSignInAccount user = FakeGoogleSignInAccount(); - when(mockAuthService.user).thenReturn(user); - - await tester.pumpWidget( - ValueProvider( - value: mockAuthService, - child: testApp, - ), - ); - await tester.pump(); - - await tester.tap(find.byType(UserSignIn)); - await tester.pumpAndSettle(); - - verifyNever(mockAuthService.signOut()); - - await tester.tap(find.text('Log out')); - - verify(mockAuthService.signOut()).called(1); - }); -} diff --git a/dashboard/test/widgets/web_image_test.dart b/dashboard/test/widgets/web_image_test.dart deleted file mode 100644 index e584f1fc4..000000000 --- a/dashboard/test/widgets/web_image_test.dart +++ /dev/null @@ -1,15 +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:flutter_dashboard/widgets/web_image.dart'; - -import 'package:flutter_test/flutter_test.dart'; - -void main() { - testWidgets('WebImage.enabled', (WidgetTester tester) async { - expect(const WebImage(imageUrl: 'url').enabled, isFalse); // because this is a test - expect(const WebImage(imageUrl: 'url', enabled: false).enabled, isFalse); - expect(const WebImage(imageUrl: 'url', enabled: true).enabled, isTrue); - }); -} diff --git a/dashboard/web/favicon-failure.png b/dashboard/web/favicon-failure.png deleted file mode 100644 index 73249095718c31f24ef06979c0eb32a6a27a9c04..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 372 zcmV-)0gL{LP)Px$EJ;K`R9HvVm(2~rFbqcN8Rmfj;J_*@zz{6J6uonR0bm|DgHp+rNuBibPbA=m zgFyMXA0;Bcf%7~! So&W#<07*qoM6N<$f&c(*HJh&h diff --git a/dashboard/web/favicon.png b/dashboard/web/favicon.png deleted file mode 100644 index f75e7a2992e73260234f8a6ffd1915f3aae5b68e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 363 zcmV-x0hIoUP)Px$BuPX;R9HvVm(L2qFbszszMjIod+^abcpV$)>xt4qM%Ja>FMkT^P00FvO`A6D z4$9wY>_4?nUYDuU3<(AAK}$eHrYc|s=R!4+qCga!WJ(gEKs3B2u@PtmFG;KfTEiuY zMZf}o_T#1i&eH{|LfR8%0S`PGt^~BR_rm#7%o)%KM8PQwDuGD&B7n|58cxg5Ap))7 zNn%$3g`;tKjaCKFLD4wrfKX*XI%GU>XbDnKg0z6c0#fR{GgnnXE)WHuGw@d@A?*jA zkzp9-zxU$MIm&M-g^@e2u-B-DoB?qO+b1Tq<5uCCv>ESfRum& zp%X;f!~1{tzL__3=gjVJ=j=J>+nMj%ncXj1Q(b|Ckbw{Y0FWpt%4y%$uD=Z*c-x~o zE;IoE;xa#7Ll5nj-e4CuXB&G*IM~D21rCP$*xLXAK8rIMCSHuSu%bL&S3)8YI~vyp@KBu9Ph7R_pvKQ@xv>NQ`dZp(u{Z8K3yOB zn7-AR+d2JkW)KiGx0hosml;+eCXp6+w%@STjFY*CJ?udJ64&{BCbuebcuH;}(($@@ znNlgBA@ZXB)mcl9nbX#F!f_5Z=W>0kh|UVWnf!At4V*LQP%*gPdCXd6P@J4Td;!Ur z<2ZLmwr(NG`u#gDEMP19UcSzRTL@HsK+PnIXbVBT@oHm53DZr?~V(0{rsalAfwgo zEh=GviaqkF;}F_5-yA!1u3!gxaR&Mj)hLuj5Q-N-@Lra{%<4ONja8pycD90&>yMB` zchhd>0CsH`^|&TstH-8+R`CfoWqmTTF_0?zDOY`E`b)cVi!$4xA@oO;SyOjJyP^_j zx^@Gdf+w|FW@DMdOi8=4+LJl$#@R&&=UM`)G!y%6ZzQLoSL%*KE8IO0~&5XYR9 z&N)?goEiWA(YoRfT{06&D6Yuu@Qt&XVbuW@COb;>SP9~aRc+z`m`80pB2o%`#{xD@ zI3RAlukL5L>px6b?QW1Ac_0>ew%NM!XB2(H+1Y3AJC?C?O`GGs`331Nd4ZvG~bMo{lh~GeL zSL|tT*fF-HXxXYtfu5z+T5Mx9OdP7J4g%@oeC2FaWO1D{=NvL|DNZ}GO?O3`+H*SI z=grGv=7dL{+oY0eJFGO!Qe(e2F?CHW(i!!XkGo2tUvsQ)I9ev`H&=;`N%Z{L zO?vV%rDv$y(@1Yj@xfr7Kzr<~0{^T8wM80xf7IGQF_S-2c0)0D6b0~yD7BsCy+(zL z#N~%&e4iAwi4F$&dI7x6cE|B{f@lY5epaDh=2-(4N05VO~A zQT3hanGy_&p+7Fb^I#ewGsjyCEUmSCaP6JDB*=_()FgQ(-pZ28-{qx~2foO4%pM9e z*_63RT8XjgiaWY|*xydf;8MKLd{HnfZ2kM%iq}fstImB-K6A79B~YoPVa@tYN@T_$ zea+9)<%?=Fl!kd(Y!G(-o}ko28hg2!MR-o5BEa_72uj7Mrc&{lRh3u2%Y=Xk9^-qa zBPWaD=2qcuJ&@Tf6ue&)4_V*45=zWk@Z}Q?f5)*z)-+E|-yC4fs5CE6L_PH3=zI8p z*Z3!it{1e5_^(sF*v=0{`U9C741&lub89gdhKp|Y8CeC{_{wYK-LSbp{h)b~9^j!s z7e?Y{Z3pZv0J)(VL=g>l;<}xk=T*O5YR|hg0eg4u98f2IrA-MY+StQIuK-(*J6TRR z|IM(%uI~?`wsfyO6Tgmsy1b3a)j6M&-jgUjVg+mP*oTKdHg?5E`!r`7AE_#?Fc)&a z08KCq>Gc=ne{PCbRvs6gVW|tKdcE1#7C4e`M|j$C5EYZ~Y=jUtc zj`+?p4ba3uy7><7wIokM79jPza``{Lx0)zGWg;FW1^NKY+GpEi=rHJ+fVRGfXO zPHV52k?jxei_!YYAw1HIz}y8ZMwdZqU%ESwMn7~t zdI5%B;U7RF=jzRz^NuY9nM)&<%M>x>0(e$GpU9th%rHiZsIT>_qp%V~ILlyt^V`=d z!1+DX@ah?RnB$X!0xpTA0}lN@9V-ePx>wQ?-xrJr^qDlw?#O(RsXeAvM%}rg0NT#t z!CsT;-vB=B87ShG`GwO;OEbeL;a}LIu=&@9cb~Rsx(ZPNQ!NT7H{@j0e(DiLea>QD zPmpe90gEKHEZ8oQ@6%E7k-Ptn#z)b9NbD@_GTxEhbS+}Bb74WUaRy{w;E|MgDAvHw zL)ycgM7mB?XVh^OzbC?LKFMotw3r@i&VdUV%^Efdib)3@soX%vWCbnOyt@Y4swW925@bt45y0HY3YI~BnnzZYrinFy;L?2D3BAL`UQ zEj))+f>H7~g8*VuWQ83EtGcx`hun$QvuurSMg3l4IP8Fe`#C|N6mbYJ=n;+}EQm;< z!!N=5j1aAr_uEnnzrEV%_E|JpTb#1p1*}5!Ce!R@d$EtMR~%9# zd;h8=QGT)KMW2IKu_fA_>p_und#-;Q)p%%l0XZOXQicfX8M~7?8}@U^ihu;mizj)t zgV7wk%n-UOb z#!P5q?Ex+*Kx@*p`o$q8FWL*E^$&1*!gpv?Za$YO~{BHeGY*5%4HXUKa_A~~^d z=E*gf6&+LFF^`j4$T~dR)%{I)T?>@Ma?D!gi9I^HqvjPc3-v~=qpX1Mne@*rzT&Xw zQ9DXsSV@PqpEJO-g4A&L{F&;K6W60D!_vs?Vx!?w27XbEuJJP&);)^+VF1nHqHBWu z^>kI$M9yfOY8~|hZ9WB!q-9u&mKhEcRjlf2nm_@s;0D#c|@ED7NZE% zzR;>P5B{o4fzlfsn3CkBK&`OSb-YNrqx@N#4CK!>bQ(V(D#9|l!e9(%sz~PYk@8zt zPN9oK78&-IL_F zhsk1$6p;GqFbtB^ZHHP+cjMvA0(LqlskbdYE_rda>gvQLTiqOQ1~*7lg%z*&p`Ry& zRcG^DbbPj_jOKHTr8uk^15Boj6>hA2S-QY(W-6!FIq8h$<>MI>PYYRenQDBamO#Fv zAH5&ImqKBDn0v5kb|8i0wFhUBJTpT!rB-`zK)^SNnRmLraZcPYK7b{I@+}wXVdW-{Ps17qdRA3JatEd?rPV z4@}(DAMf5EqXCr4-B+~H1P#;t@O}B)tIJ(W6$LrK&0plTmnPpb1TKn3?f?Kk``?D+ zQ!MFqOX7JbsXfQrz`-M@hq7xlfNz;_B{^wbpG8des56x(Q)H)5eLeDwCrVR}hzr~= zM{yXR6IM?kXxauLza#@#u?Y|o;904HCqF<8yT~~c-xyRc0-vxofnxG^(x%>bj5r}N zyFT+xnn-?B`ohA>{+ZZQem=*Xpqz{=j8i2TAC#x-m;;mo{{sLB_z(UoAqD=A#*juZ zCv=J~i*O8;F}A^Wf#+zx;~3B{57xtoxC&j^ie^?**T`WT2OPRtC`xj~+3Kprn=rVM zVJ|h5ux%S{dO}!mq93}P+h36mZ5aZg1-?vhL$ke1d52qIiXSE(llCr5i=QUS?LIjc zV$4q=-)aaR4wsrQv}^shL5u%6;`uiSEs<1nG^?$kl$^6DL z43CjY`M*p}ew}}3rXc7Xck@k41jx}c;NgEIhKZ*jsBRZUP-x2cm;F1<5$jefl|ppO zmZd%%?gMJ^g9=RZ^#8Mf5aWNVhjAS^|DQO+q$)oeob_&ZLFL(zur$)); zU19yRm)z<4&4-M}7!9+^Wl}Uk?`S$#V2%pQ*SIH5KI-mn%i;Z7-)m$mN9CnI$G7?# zo`zVrUwoSL&_dJ92YhX5TKqaRkfPgC4=Q&=K+;_aDs&OU0&{WFH}kKX6uNQC6%oUH z2DZa1s3%Vtk|bglbxep-w)PbFG!J17`<$g8lVhqD2w;Z0zGsh-r zxZ13G$G<48leNqR!DCVt9)@}(zMI5w6Wo=N zpP1*3DI;~h2WDWgcKn*f!+ORD)f$DZFwgKBafEZmeXQMAsq9sxP9A)7zOYnkHT9JU zRA`umgmP9d6=PHmFIgx=0$(sjb>+0CHG)K@cPG{IxaJ&Ueo8)0RWgV9+gO7+Bl1(F z7!BslJ2MP*PWJ;x)QXbR$6jEr5q3 z(3}F@YO_P1NyTdEXRLU6fp?9V2-S=E+YaeLL{Y)W%6`k7$(EW8EZSA*(+;e5@jgD^I zaJQ2|oCM1n!A&-8`;#RDcZyk*+RPkn_r8?Ak@agHiSp*qFNX)&i21HE?yuZ;-C<3C zwJGd1lx5UzViP7sZJ&|LqH*mryb}y|%AOw+v)yc`qM)03qyyrqhX?ub`Cjwx2PrR! z)_z>5*!*$x1=Qa-0uE7jy0z`>|Ni#X+uV|%_81F7)b+nf%iz=`fF4g5UfHS_?PHbr zB;0$bK@=di?f`dS(j{l3-tSCfp~zUuva+=EWxJcRfp(<$@vd(GigM&~vaYZ0c#BTs z3ijkxMl=vw5AS&DcXQ%eeKt!uKvh2l3W?&3=dBHU=Gz?O!40S&&~ei2vg**c$o;i89~6DVns zG>9a*`k5)NI9|?W!@9>rzJ;9EJ=YlJTx1r1BA?H`LWijk(rTax9(OAu;q4_wTj-yj z1%W4GW&K4T=uEGb+E!>W0SD_C0RR91 diff --git a/dashboard/web/icons/Icon-512.png b/dashboard/web/icons/Icon-512.png deleted file mode 100644 index 88cfd48dff1169879ba46840804b412fe02fefd6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8252 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 diff --git a/dashboard/web/icons/Icon-maskable-192.png b/dashboard/web/icons/Icon-maskable-192.png deleted file mode 100644 index eb9b4d76e525556d5d89141648c724331630325d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5594 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 X>c!)3PgKfL&lX^$Z>Cpu&6)6jvi^Z! diff --git a/dashboard/web/icons/Icon-maskable-512.png b/dashboard/web/icons/Icon-maskable-512.png deleted file mode 100644 index d69c56691fbdb0b7efa65097c7cc1edac12a6d3e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20998 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 MPgg&ebxsLQ025IeI{*Lx diff --git a/dashboard/web/index.html b/dashboard/web/index.html deleted file mode 100644 index 74310d2a6..000000000 --- a/dashboard/web/index.html +++ /dev/null @@ -1,45 +0,0 @@ - - - - - - - Flutter Build Dashboard — Cocoon - - - - - - - - - - - - - - - - - - diff --git a/dashboard/web/manifest.json b/dashboard/web/manifest.json deleted file mode 100644 index b61fa5779..000000000 --- a/dashboard/web/manifest.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "app_flutter", - "short_name": "app_flutter", - "start_url": ".", - "display": "standalone", - "background_color": "#0175C2", - "theme_color": "#0175C2", - "description": "A new Flutter project.", - "orientation": "portrait-primary", - "prefer_related_applications": false, - "icons": [ - { - "src": "icons/Icon-192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "icons/Icon-512.png", - "sizes": "512x512", - "type": "image/png" - } - ] -} diff --git a/dashboard/windows/.gitignore b/dashboard/windows/.gitignore deleted file mode 100644 index d492d0d98..000000000 --- a/dashboard/windows/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -flutter/ephemeral/ - -# Visual Studio user-specific files. -*.suo -*.user -*.userosscache -*.sln.docstates - -# Visual Studio build-related files. -x64/ -x86/ - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ diff --git a/dashboard/windows/CMakeLists.txt b/dashboard/windows/CMakeLists.txt deleted file mode 100644 index b687e0f76..000000000 --- a/dashboard/windows/CMakeLists.txt +++ /dev/null @@ -1,95 +0,0 @@ -cmake_minimum_required(VERSION 3.15) -project(app_flutter LANGUAGES CXX) - -set(BINARY_NAME "app_flutter") - -cmake_policy(SET CMP0063 NEW) - -set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") - -# Configure build options. -get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) -if(IS_MULTICONFIG) - set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" - CACHE STRING "" FORCE) -else() - if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - set(CMAKE_BUILD_TYPE "Debug" CACHE - STRING "Flutter build mode" FORCE) - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS - "Debug" "Profile" "Release") - endif() -endif() - -set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") -set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") -set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") -set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") - -# Use Unicode for all projects. -add_definitions(-DUNICODE -D_UNICODE) - -# Compilation settings that should be applied to most targets. -function(APPLY_STANDARD_SETTINGS TARGET) - target_compile_features(${TARGET} PUBLIC cxx_std_17) - target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") - target_compile_options(${TARGET} PRIVATE /EHsc) - target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") - target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") -endfunction() - -set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") - -# Flutter library and tool build rules. -add_subdirectory(${FLUTTER_MANAGED_DIR}) - -# Application build -add_subdirectory("runner") - -# Generated plugin build rules, which manage building the plugins and adding -# them to the application. -include(flutter/generated_plugins.cmake) - - -# === Installation === -# Support files are copied into place next to the executable, so that it can -# run in place. This is done instead of making a separate bundle (as on Linux) -# so that building and running from within Visual Studio will work. -set(BUILD_BUNDLE_DIR "$") -# Make the "install" step default, as it's required to run. -set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) -if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) -endif() - -set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") -set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") - -install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" - COMPONENT Runtime) - -install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) - -if(PLUGIN_BUNDLED_LIBRARIES) - install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" - DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" - COMPONENT Runtime) -endif() - -# Fully re-copy the assets directory on each build to avoid having stale files -# from a previous install. -set(FLUTTER_ASSET_DIR_NAME "flutter_assets") -install(CODE " - file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") - " COMPONENT Runtime) -install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" - DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) - -# Install the AOT library on non-Debug builds only. -install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" - CONFIGURATIONS Profile;Release - COMPONENT Runtime) diff --git a/dashboard/windows/flutter/CMakeLists.txt b/dashboard/windows/flutter/CMakeLists.txt deleted file mode 100644 index 744f08a93..000000000 --- a/dashboard/windows/flutter/CMakeLists.txt +++ /dev/null @@ -1,102 +0,0 @@ -cmake_minimum_required(VERSION 3.15) - -set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") - -# Configuration provided via flutter tool. -include(${EPHEMERAL_DIR}/generated_config.cmake) - -# TODO: Move the rest of this into files in ephemeral. See -# https://github.com/flutter/flutter/issues/57146. -set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") - -# === Flutter Library === -set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") - -# Published to parent scope for install step. -set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) -set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) -set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) -set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) - -list(APPEND FLUTTER_LIBRARY_HEADERS - "flutter_export.h" - "flutter_windows.h" - "flutter_messenger.h" - "flutter_plugin_registrar.h" -) -list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") -add_library(flutter INTERFACE) -target_include_directories(flutter INTERFACE - "${EPHEMERAL_DIR}" -) -target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") -add_dependencies(flutter flutter_assemble) - -# === Wrapper === -list(APPEND CPP_WRAPPER_SOURCES_CORE - "core_implementations.cc" - "standard_codec.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") -list(APPEND CPP_WRAPPER_SOURCES_PLUGIN - "plugin_registrar.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") -list(APPEND CPP_WRAPPER_SOURCES_APP - "flutter_engine.cc" - "flutter_view_controller.cc" -) -list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") - -# Wrapper sources needed for a plugin. -add_library(flutter_wrapper_plugin STATIC - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_PLUGIN} -) -apply_standard_settings(flutter_wrapper_plugin) -set_target_properties(flutter_wrapper_plugin PROPERTIES - POSITION_INDEPENDENT_CODE ON) -set_target_properties(flutter_wrapper_plugin PROPERTIES - CXX_VISIBILITY_PRESET hidden) -target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) -target_include_directories(flutter_wrapper_plugin PUBLIC - "${WRAPPER_ROOT}/include" -) -add_dependencies(flutter_wrapper_plugin flutter_assemble) - -# Wrapper sources needed for the runner. -add_library(flutter_wrapper_app STATIC - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_APP} -) -apply_standard_settings(flutter_wrapper_app) -target_link_libraries(flutter_wrapper_app PUBLIC flutter) -target_include_directories(flutter_wrapper_app PUBLIC - "${WRAPPER_ROOT}/include" -) -add_dependencies(flutter_wrapper_app flutter_assemble) - -# === Flutter tool backend === -# _phony_ is a non-existent file to force this command to run every time, -# since currently there's no way to get a full input/output list from the -# flutter tool. -set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") -set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) -add_custom_command( - OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} - ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} - ${CPP_WRAPPER_SOURCES_APP} - ${PHONY_OUTPUT} - COMMAND ${CMAKE_COMMAND} -E env - ${FLUTTER_TOOL_ENVIRONMENT} - "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ - VERBATIM -) -add_custom_target(flutter_assemble DEPENDS - "${FLUTTER_LIBRARY}" - ${FLUTTER_LIBRARY_HEADERS} - ${CPP_WRAPPER_SOURCES_CORE} - ${CPP_WRAPPER_SOURCES_PLUGIN} - ${CPP_WRAPPER_SOURCES_APP} -) diff --git a/dashboard/windows/flutter/generated_plugins.cmake b/dashboard/windows/flutter/generated_plugins.cmake deleted file mode 100644 index 88b22e5c7..000000000 --- a/dashboard/windows/flutter/generated_plugins.cmake +++ /dev/null @@ -1,24 +0,0 @@ -# -# Generated file, do not edit. -# - -list(APPEND FLUTTER_PLUGIN_LIST - url_launcher_windows -) - -list(APPEND FLUTTER_FFI_PLUGIN_LIST -) - -set(PLUGIN_BUNDLED_LIBRARIES) - -foreach(plugin ${FLUTTER_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) - target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) - list(APPEND PLUGIN_BUNDLED_LIBRARIES $) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) -endforeach(plugin) - -foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) - add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) - list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) -endforeach(ffi_plugin) diff --git a/dashboard/windows/runner/CMakeLists.txt b/dashboard/windows/runner/CMakeLists.txt deleted file mode 100644 index 977e38b5d..000000000 --- a/dashboard/windows/runner/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -cmake_minimum_required(VERSION 3.15) -project(runner LANGUAGES CXX) - -add_executable(${BINARY_NAME} WIN32 - "flutter_window.cpp" - "main.cpp" - "run_loop.cpp" - "utils.cpp" - "win32_window.cpp" - "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" - "Runner.rc" - "runner.exe.manifest" -) -apply_standard_settings(${BINARY_NAME}) -target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") -target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) -target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") -add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/dashboard/windows/runner/Runner.rc b/dashboard/windows/runner/Runner.rc deleted file mode 100644 index 0b365d495..000000000 --- a/dashboard/windows/runner/Runner.rc +++ /dev/null @@ -1,121 +0,0 @@ -// Microsoft Visual C++ generated resource script. -// -#pragma code_page(65001) -#include "resource.h" - -#define APSTUDIO_READONLY_SYMBOLS -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 2 resource. -// -#include "winres.h" - -///////////////////////////////////////////////////////////////////////////// -#undef APSTUDIO_READONLY_SYMBOLS - -///////////////////////////////////////////////////////////////////////////// -// English (United States) resources - -#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) -LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US - -#ifdef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// TEXTINCLUDE -// - -1 TEXTINCLUDE -BEGIN - "resource.h\0" -END - -2 TEXTINCLUDE -BEGIN - "#include ""winres.h""\r\n" - "\0" -END - -3 TEXTINCLUDE -BEGIN - "\r\n" - "\0" -END - -#endif // APSTUDIO_INVOKED - - -///////////////////////////////////////////////////////////////////////////// -// -// Icon -// - -// Icon with lowest ID value placed first to ensure application icon -// remains consistent on all systems. -IDI_APP_ICON ICON "resources\\app_icon.ico" - - -///////////////////////////////////////////////////////////////////////////// -// -// Version -// - -#ifdef FLUTTER_BUILD_NUMBER -#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER -#else -#define VERSION_AS_NUMBER 1,0,0 -#endif - -#ifdef FLUTTER_BUILD_NAME -#define VERSION_AS_STRING #FLUTTER_BUILD_NAME -#else -#define VERSION_AS_STRING "1.0.0" -#endif - -VS_VERSION_INFO VERSIONINFO - FILEVERSION VERSION_AS_NUMBER - PRODUCTVERSION VERSION_AS_NUMBER - FILEFLAGSMASK VS_FFI_FILEFLAGSMASK -#ifdef _DEBUG - FILEFLAGS VS_FF_DEBUG -#else - FILEFLAGS 0x0L -#endif - FILEOS VOS__WINDOWS32 - FILETYPE VFT_APP - FILESUBTYPE 0x0L -BEGIN - BLOCK "StringFileInfo" - BEGIN - BLOCK "040904e4" - BEGIN - VALUE "CompanyName", "com.example" "\0" - VALUE "FileDescription", "A new Flutter project." "\0" - VALUE "FileVersion", VERSION_AS_STRING "\0" - VALUE "InternalName", "app_flutter" "\0" - VALUE "LegalCopyright", "Copyright (C) 2021 com.example. All rights reserved." "\0" - VALUE "OriginalFilename", "app_flutter.exe" "\0" - VALUE "ProductName", "app_flutter" "\0" - VALUE "ProductVersion", VERSION_AS_STRING "\0" - END - END - BLOCK "VarFileInfo" - BEGIN - VALUE "Translation", 0x409, 1252 - END -END - -#endif // English (United States) resources -///////////////////////////////////////////////////////////////////////////// - - - -#ifndef APSTUDIO_INVOKED -///////////////////////////////////////////////////////////////////////////// -// -// Generated from the TEXTINCLUDE 3 resource. -// - - -///////////////////////////////////////////////////////////////////////////// -#endif // not APSTUDIO_INVOKED diff --git a/dashboard/windows/runner/flutter_window.cpp b/dashboard/windows/runner/flutter_window.cpp deleted file mode 100644 index c42272304..000000000 --- a/dashboard/windows/runner/flutter_window.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "flutter_window.h" - -#include - -#include "flutter/generated_plugin_registrant.h" - -FlutterWindow::FlutterWindow(RunLoop* run_loop, - const flutter::DartProject& project) - : run_loop_(run_loop), project_(project) {} - -FlutterWindow::~FlutterWindow() {} - -bool FlutterWindow::OnCreate() { - if (!Win32Window::OnCreate()) { - return false; - } - - RECT frame = GetClientArea(); - - // The size here must match the window dimensions to avoid unnecessary surface - // creation / destruction in the startup path. - flutter_controller_ = std::make_unique( - frame.right - frame.left, frame.bottom - frame.top, project_); - // Ensure that basic setup of the controller was successful. - if (!flutter_controller_->engine() || !flutter_controller_->view()) { - return false; - } - RegisterPlugins(flutter_controller_->engine()); - run_loop_->RegisterFlutterInstance(flutter_controller_->engine()); - SetChildContent(flutter_controller_->view()->GetNativeWindow()); - return true; -} - -void FlutterWindow::OnDestroy() { - if (flutter_controller_) { - run_loop_->UnregisterFlutterInstance(flutter_controller_->engine()); - flutter_controller_ = nullptr; - } - - Win32Window::OnDestroy(); -} - -LRESULT -FlutterWindow::MessageHandler(HWND hwnd, UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - // Give Flutter, including plugins, an opporutunity to handle window messages. - if (flutter_controller_) { - std::optional result = - flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, - lparam); - if (result) { - return *result; - } - } - - switch (message) { - case WM_FONTCHANGE: - flutter_controller_->engine()->ReloadSystemFonts(); - break; - } - - return Win32Window::MessageHandler(hwnd, message, wparam, lparam); -} diff --git a/dashboard/windows/runner/flutter_window.h b/dashboard/windows/runner/flutter_window.h deleted file mode 100644 index b663ddd50..000000000 --- a/dashboard/windows/runner/flutter_window.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef RUNNER_FLUTTER_WINDOW_H_ -#define RUNNER_FLUTTER_WINDOW_H_ - -#include -#include - -#include - -#include "run_loop.h" -#include "win32_window.h" - -// A window that does nothing but host a Flutter view. -class FlutterWindow : public Win32Window { - public: - // Creates a new FlutterWindow driven by the |run_loop|, hosting a - // Flutter view running |project|. - explicit FlutterWindow(RunLoop* run_loop, - const flutter::DartProject& project); - virtual ~FlutterWindow(); - - protected: - // Win32Window: - bool OnCreate() override; - void OnDestroy() override; - LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, - LPARAM const lparam) noexcept override; - - private: - // The run loop driving events for this window. - RunLoop* run_loop_; - - // The project to run. - flutter::DartProject project_; - - // The Flutter instance hosted by this window. - std::unique_ptr flutter_controller_; -}; - -#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/dashboard/windows/runner/main.cpp b/dashboard/windows/runner/main.cpp deleted file mode 100644 index 59157d0cb..000000000 --- a/dashboard/windows/runner/main.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include -#include -#include - -#include "flutter_window.h" -#include "run_loop.h" -#include "utils.h" - -int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, - _In_ wchar_t *command_line, _In_ int show_command) { - // Attach to console when present (e.g., 'flutter run') or create a - // new console when running with a debugger. - if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { - CreateAndAttachConsole(); - } - - // Initialize COM, so that it is available for use in the library and/or - // plugins. - ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); - - RunLoop run_loop; - - flutter::DartProject project(L"data"); - - std::vector command_line_arguments = - GetCommandLineArguments(); - - project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); - - FlutterWindow window(&run_loop, project); - Win32Window::Point origin(10, 10); - Win32Window::Size size(1280, 720); - if (!window.CreateAndShow(L"app_flutter", origin, size)) { - return EXIT_FAILURE; - } - window.SetQuitOnClose(true); - - run_loop.Run(); - - ::CoUninitialize(); - return EXIT_SUCCESS; -} diff --git a/dashboard/windows/runner/resource.h b/dashboard/windows/runner/resource.h deleted file mode 100644 index 66a65d1e4..000000000 --- a/dashboard/windows/runner/resource.h +++ /dev/null @@ -1,16 +0,0 @@ -//{{NO_DEPENDENCIES}} -// Microsoft Visual C++ generated include file. -// Used by Runner.rc -// -#define IDI_APP_ICON 101 - -// Next default values for new objects -// -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 102 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1001 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif diff --git a/dashboard/windows/runner/resources/app_icon.ico b/dashboard/windows/runner/resources/app_icon.ico deleted file mode 100644 index c04e20caf6370ebb9253ad831cc31de4a9c965f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33772 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 diff --git a/dashboard/windows/runner/run_loop.cpp b/dashboard/windows/runner/run_loop.cpp deleted file mode 100644 index 2d6636ab6..000000000 --- a/dashboard/windows/runner/run_loop.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include "run_loop.h" - -#include - -#include - -RunLoop::RunLoop() {} - -RunLoop::~RunLoop() {} - -void RunLoop::Run() { - bool keep_running = true; - TimePoint next_flutter_event_time = TimePoint::clock::now(); - while (keep_running) { - std::chrono::nanoseconds wait_duration = - std::max(std::chrono::nanoseconds(0), - next_flutter_event_time - TimePoint::clock::now()); - ::MsgWaitForMultipleObjects( - 0, nullptr, FALSE, static_cast(wait_duration.count() / 1000), - QS_ALLINPUT); - bool processed_events = false; - MSG message; - // All pending Windows messages must be processed; MsgWaitForMultipleObjects - // won't return again for items left in the queue after PeekMessage. - while (::PeekMessage(&message, nullptr, 0, 0, PM_REMOVE)) { - processed_events = true; - if (message.message == WM_QUIT) { - keep_running = false; - break; - } - ::TranslateMessage(&message); - ::DispatchMessage(&message); - // Allow Flutter to process messages each time a Windows message is - // processed, to prevent starvation. - next_flutter_event_time = - std::min(next_flutter_event_time, ProcessFlutterMessages()); - } - // If the PeekMessage loop didn't run, process Flutter messages. - if (!processed_events) { - next_flutter_event_time = - std::min(next_flutter_event_time, ProcessFlutterMessages()); - } - } -} - -void RunLoop::RegisterFlutterInstance( - flutter::FlutterEngine* flutter_instance) { - flutter_instances_.insert(flutter_instance); -} - -void RunLoop::UnregisterFlutterInstance( - flutter::FlutterEngine* flutter_instance) { - flutter_instances_.erase(flutter_instance); -} - -RunLoop::TimePoint RunLoop::ProcessFlutterMessages() { - TimePoint next_event_time = TimePoint::max(); - for (auto instance : flutter_instances_) { - std::chrono::nanoseconds wait_duration = instance->ProcessMessages(); - if (wait_duration != std::chrono::nanoseconds::max()) { - next_event_time = - std::min(next_event_time, TimePoint::clock::now() + wait_duration); - } - } - return next_event_time; -} diff --git a/dashboard/windows/runner/run_loop.h b/dashboard/windows/runner/run_loop.h deleted file mode 100644 index 000d36246..000000000 --- a/dashboard/windows/runner/run_loop.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef RUNNER_RUN_LOOP_H_ -#define RUNNER_RUN_LOOP_H_ - -#include - -#include -#include - -// A runloop that will service events for Flutter instances as well -// as native messages. -class RunLoop { - public: - RunLoop(); - ~RunLoop(); - - // Prevent copying - RunLoop(RunLoop const&) = delete; - RunLoop& operator=(RunLoop const&) = delete; - - // Runs the run loop until the application quits. - void Run(); - - // Registers the given Flutter instance for event servicing. - void RegisterFlutterInstance( - flutter::FlutterEngine* flutter_instance); - - // Unregisters the given Flutter instance from event servicing. - void UnregisterFlutterInstance( - flutter::FlutterEngine* flutter_instance); - - private: - using TimePoint = std::chrono::steady_clock::time_point; - - // Processes all currently pending messages for registered Flutter instances. - TimePoint ProcessFlutterMessages(); - - std::set flutter_instances_; -}; - -#endif // RUNNER_RUN_LOOP_H_ diff --git a/dashboard/windows/runner/runner.exe.manifest b/dashboard/windows/runner/runner.exe.manifest deleted file mode 100644 index c977c4a42..000000000 --- a/dashboard/windows/runner/runner.exe.manifest +++ /dev/null @@ -1,20 +0,0 @@ - - - - - PerMonitorV2 - - - - - - - - - - - - - - - diff --git a/dashboard/windows/runner/utils.cpp b/dashboard/windows/runner/utils.cpp deleted file mode 100644 index d19bdbbcc..000000000 --- a/dashboard/windows/runner/utils.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "utils.h" - -#include -#include -#include -#include - -#include - -void CreateAndAttachConsole() { - if (::AllocConsole()) { - FILE *unused; - if (freopen_s(&unused, "CONOUT$", "w", stdout)) { - _dup2(_fileno(stdout), 1); - } - if (freopen_s(&unused, "CONOUT$", "w", stderr)) { - _dup2(_fileno(stdout), 2); - } - std::ios::sync_with_stdio(); - FlutterDesktopResyncOutputStreams(); - } -} - -std::vector GetCommandLineArguments() { - // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. - int argc; - wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); - if (argv == nullptr) { - return std::vector(); - } - - std::vector command_line_arguments; - - // Skip the first argument as it's the binary name. - for (int i = 1; i < argc; i++) { - command_line_arguments.push_back(Utf8FromUtf16(argv[i])); - } - - ::LocalFree(argv); - - return command_line_arguments; -} - -std::string Utf8FromUtf16(const wchar_t* utf16_string) { - if (utf16_string == nullptr) { - return std::string(); - } - int target_length = ::WideCharToMultiByte( - CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, - -1, nullptr, 0, nullptr, nullptr); - if (target_length == 0) { - return std::string(); - } - std::string utf8_string; - utf8_string.resize(target_length); - int converted_length = ::WideCharToMultiByte( - CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, - -1, utf8_string.data(), - target_length, nullptr, nullptr); - if (converted_length == 0) { - return std::string(); - } - return utf8_string; -} diff --git a/dashboard/windows/runner/utils.h b/dashboard/windows/runner/utils.h deleted file mode 100644 index 3879d5475..000000000 --- a/dashboard/windows/runner/utils.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef RUNNER_UTILS_H_ -#define RUNNER_UTILS_H_ - -#include -#include - -// Creates a console for the process, and redirects stdout and stderr to -// it for both the runner and the Flutter library. -void CreateAndAttachConsole(); - -// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string -// encoded in UTF-8. Returns an empty std::string on failure. -std::string Utf8FromUtf16(const wchar_t* utf16_string); - -// Gets the command line arguments passed in as a std::vector, -// encoded in UTF-8. Returns an empty std::vector on failure. -std::vector GetCommandLineArguments(); - -#endif // RUNNER_UTILS_H_ diff --git a/dashboard/windows/runner/win32_window.cpp b/dashboard/windows/runner/win32_window.cpp deleted file mode 100644 index c10f08dc7..000000000 --- a/dashboard/windows/runner/win32_window.cpp +++ /dev/null @@ -1,245 +0,0 @@ -#include "win32_window.h" - -#include - -#include "resource.h" - -namespace { - -constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; - -// The number of Win32Window objects that currently exist. -static int g_active_window_count = 0; - -using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); - -// Scale helper to convert logical scaler values to physical using passed in -// scale factor -int Scale(int source, double scale_factor) { - return static_cast(source * scale_factor); -} - -// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. -// This API is only needed for PerMonitor V1 awareness mode. -void EnableFullDpiSupportIfAvailable(HWND hwnd) { - HMODULE user32_module = LoadLibraryA("User32.dll"); - if (!user32_module) { - return; - } - auto enable_non_client_dpi_scaling = - reinterpret_cast( - GetProcAddress(user32_module, "EnableNonClientDpiScaling")); - if (enable_non_client_dpi_scaling != nullptr) { - enable_non_client_dpi_scaling(hwnd); - FreeLibrary(user32_module); - } -} - -} // namespace - -// Manages the Win32Window's window class registration. -class WindowClassRegistrar { - public: - ~WindowClassRegistrar() = default; - - // Returns the singleton registar instance. - static WindowClassRegistrar* GetInstance() { - if (!instance_) { - instance_ = new WindowClassRegistrar(); - } - return instance_; - } - - // Returns the name of the window class, registering the class if it hasn't - // previously been registered. - const wchar_t* GetWindowClass(); - - // Unregisters the window class. Should only be called if there are no - // instances of the window. - void UnregisterWindowClass(); - - private: - WindowClassRegistrar() = default; - - static WindowClassRegistrar* instance_; - - bool class_registered_ = false; -}; - -WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; - -const wchar_t* WindowClassRegistrar::GetWindowClass() { - if (!class_registered_) { - WNDCLASS window_class{}; - window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); - window_class.lpszClassName = kWindowClassName; - window_class.style = CS_HREDRAW | CS_VREDRAW; - window_class.cbClsExtra = 0; - window_class.cbWndExtra = 0; - window_class.hInstance = GetModuleHandle(nullptr); - window_class.hIcon = - LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); - window_class.hbrBackground = 0; - window_class.lpszMenuName = nullptr; - window_class.lpfnWndProc = Win32Window::WndProc; - RegisterClass(&window_class); - class_registered_ = true; - } - return kWindowClassName; -} - -void WindowClassRegistrar::UnregisterWindowClass() { - UnregisterClass(kWindowClassName, nullptr); - class_registered_ = false; -} - -Win32Window::Win32Window() { - ++g_active_window_count; -} - -Win32Window::~Win32Window() { - --g_active_window_count; - Destroy(); -} - -bool Win32Window::CreateAndShow(const std::wstring& title, - const Point& origin, - const Size& size) { - Destroy(); - - const wchar_t* window_class = - WindowClassRegistrar::GetInstance()->GetWindowClass(); - - const POINT target_point = {static_cast(origin.x), - static_cast(origin.y)}; - HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); - UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); - double scale_factor = dpi / 96.0; - - HWND window = CreateWindow( - window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE, - Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), - Scale(size.width, scale_factor), Scale(size.height, scale_factor), - nullptr, nullptr, GetModuleHandle(nullptr), this); - - if (!window) { - return false; - } - - return OnCreate(); -} - -// static -LRESULT CALLBACK Win32Window::WndProc(HWND const window, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - if (message == WM_NCCREATE) { - auto window_struct = reinterpret_cast(lparam); - SetWindowLongPtr(window, GWLP_USERDATA, - reinterpret_cast(window_struct->lpCreateParams)); - - auto that = static_cast(window_struct->lpCreateParams); - EnableFullDpiSupportIfAvailable(window); - that->window_handle_ = window; - } else if (Win32Window* that = GetThisFromHandle(window)) { - return that->MessageHandler(window, message, wparam, lparam); - } - - return DefWindowProc(window, message, wparam, lparam); -} - -LRESULT -Win32Window::MessageHandler(HWND hwnd, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept { - switch (message) { - case WM_DESTROY: - window_handle_ = nullptr; - Destroy(); - if (quit_on_close_) { - PostQuitMessage(0); - } - return 0; - - case WM_DPICHANGED: { - auto newRectSize = reinterpret_cast(lparam); - LONG newWidth = newRectSize->right - newRectSize->left; - LONG newHeight = newRectSize->bottom - newRectSize->top; - - SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, - newHeight, SWP_NOZORDER | SWP_NOACTIVATE); - - return 0; - } - case WM_SIZE: { - RECT rect = GetClientArea(); - if (child_content_ != nullptr) { - // Size and position the child window. - MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, - rect.bottom - rect.top, TRUE); - } - return 0; - } - - case WM_ACTIVATE: - if (child_content_ != nullptr) { - SetFocus(child_content_); - } - return 0; - } - - return DefWindowProc(window_handle_, message, wparam, lparam); -} - -void Win32Window::Destroy() { - OnDestroy(); - - if (window_handle_) { - DestroyWindow(window_handle_); - window_handle_ = nullptr; - } - if (g_active_window_count == 0) { - WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); - } -} - -Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { - return reinterpret_cast( - GetWindowLongPtr(window, GWLP_USERDATA)); -} - -void Win32Window::SetChildContent(HWND content) { - child_content_ = content; - SetParent(content, window_handle_); - RECT frame = GetClientArea(); - - MoveWindow(content, frame.left, frame.top, frame.right - frame.left, - frame.bottom - frame.top, true); - - SetFocus(child_content_); -} - -RECT Win32Window::GetClientArea() { - RECT frame; - GetClientRect(window_handle_, &frame); - return frame; -} - -HWND Win32Window::GetHandle() { - return window_handle_; -} - -void Win32Window::SetQuitOnClose(bool quit_on_close) { - quit_on_close_ = quit_on_close; -} - -bool Win32Window::OnCreate() { - // No-op; provided for subclasses. - return true; -} - -void Win32Window::OnDestroy() { - // No-op; provided for subclasses. -} diff --git a/dashboard/windows/runner/win32_window.h b/dashboard/windows/runner/win32_window.h deleted file mode 100644 index 17ba43112..000000000 --- a/dashboard/windows/runner/win32_window.h +++ /dev/null @@ -1,98 +0,0 @@ -#ifndef RUNNER_WIN32_WINDOW_H_ -#define RUNNER_WIN32_WINDOW_H_ - -#include - -#include -#include -#include - -// A class abstraction for a high DPI-aware Win32 Window. Intended to be -// inherited from by classes that wish to specialize with custom -// rendering and input handling -class Win32Window { - public: - struct Point { - unsigned int x; - unsigned int y; - Point(unsigned int x, unsigned int y) : x(x), y(y) {} - }; - - struct Size { - unsigned int width; - unsigned int height; - Size(unsigned int width, unsigned int height) - : width(width), height(height) {} - }; - - Win32Window(); - virtual ~Win32Window(); - - // Creates and shows a win32 window with |title| and position and size using - // |origin| and |size|. New windows are created on the default monitor. Window - // sizes are specified to the OS in physical pixels, hence to ensure a - // consistent size to will treat the width height passed in to this function - // as logical pixels and scale to appropriate for the default monitor. Returns - // true if the window was created successfully. - bool CreateAndShow(const std::wstring& title, - const Point& origin, - const Size& size); - - // Release OS resources associated with window. - void Destroy(); - - // Inserts |content| into the window tree. - void SetChildContent(HWND content); - - // Returns the backing Window handle to enable clients to set icon and other - // window properties. Returns nullptr if the window has been destroyed. - HWND GetHandle(); - - // If true, closing this window will quit the application. - void SetQuitOnClose(bool quit_on_close); - - // Return a RECT representing the bounds of the current client area. - RECT GetClientArea(); - - protected: - // Processes and route salient window messages for mouse handling, - // size change and DPI. Delegates handling of these to member overloads that - // inheriting classes can handle. - virtual LRESULT MessageHandler(HWND window, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept; - - // Called when CreateAndShow is called, allowing subclass window-related - // setup. Subclasses should return false if setup fails. - virtual bool OnCreate(); - - // Called when Destroy is called. - virtual void OnDestroy(); - - private: - friend class WindowClassRegistrar; - - // OS callback called by message pump. Handles the WM_NCCREATE message which - // is passed when the non-client area is being created and enables automatic - // non-client DPI scaling so that the non-client area automatically - // responsponds to changes in DPI. All other messages are handled by - // MessageHandler. - static LRESULT CALLBACK WndProc(HWND const window, - UINT const message, - WPARAM const wparam, - LPARAM const lparam) noexcept; - - // Retrieves a class instance pointer for |window| - static Win32Window* GetThisFromHandle(HWND const window) noexcept; - - bool quit_on_close_ = false; - - // window handle for top level window. - HWND window_handle_ = nullptr; - - // window handle for hosted content. - HWND child_content_ = nullptr; -}; - -#endif // RUNNER_WIN32_WINDOW_H_ diff --git a/dev/githubanalysis/.gitignore b/dev/githubanalysis/.gitignore deleted file mode 100644 index 636451498..000000000 --- a/dev/githubanalysis/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# https://dart.dev/guides/libraries/private-files -# Created by `dart pub` -.dart_tool/ -.github-token -cache/ -output/ -members.txt -exmembers.txt diff --git a/dev/githubanalysis/README.md b/dev/githubanalysis/README.md deleted file mode 100644 index c45e63a52..000000000 --- a/dev/githubanalysis/README.md +++ /dev/null @@ -1,34 +0,0 @@ -GitHub Issues Analysis -====================== - -This is a command line program that downloads the entirety of a GitHub -project's issues database and then runs certain analyses against the -collected data. - -It's not intended for production use; instead it is used to -occasionally do scans of our repos, to learn about how things are -going, to find users who are no longer active, to audit our use of -labels, and so forth. For example, it was instrumental in the research -that led to the [Flutter project proposed new triage processes for -2023](https://flutter.dev/go/triage-2023-rfc). - -To use this tool, first create the following files in this directory: - - `.github-token`: [Personal access - token](https://github.com/settings/personal-access-tokens/new) with - the resource owner "flutter", set to have read-only access to all - public repositories, and set to have access to "Organization - permissions -> Members -> Read-only". - - `members.txt`: List of all people who are expected to be members of - the flutter-hackers group, one to a line. - - `exmembers.txt`: List of all people who were once members of the - flutter-hackers group, but are not currently members, one to a - line. - -Then, run: `dart run --enable-asserts bin/githubanalysis.dart` - -See also: - - * [Output from 2023-03-30](https://docs.google.com/spreadsheets/d/15hyxxapUmsK6J05X1goQ__9xJdzhJQM-BqsvM31-CTk/edit#gid=0) as a spreadsheet (with graphs). diff --git a/dev/githubanalysis/analysis_options.yaml b/dev/githubanalysis/analysis_options.yaml deleted file mode 100644 index 913fe311a..000000000 --- a/dev/githubanalysis/analysis_options.yaml +++ /dev/null @@ -1,211 +0,0 @@ -linter: - rules: - - always_declare_return_types - - always_put_control_body_on_new_line - - always_put_required_named_parameters_first - - always_specify_types - # - always_use_package_imports - - annotate_overrides - - avoid_annotating_with_dynamic - - avoid_bool_literals_in_conditional_expressions - - avoid_catches_without_on_clauses - - avoid_catching_errors - - avoid_classes_with_only_static_members - - avoid_double_and_int_checks - - avoid_dynamic_calls - - avoid_empty_else - - avoid_equals_and_hash_code_on_mutable_classes - # - avoid_escaping_inner_quotes - - avoid_field_initializers_in_const_classes - # - avoid_final_parameters - - avoid_function_literals_in_foreach_calls - - avoid_implementing_value_types - - avoid_init_to_null - - avoid_js_rounded_ints - - avoid_multiple_declarations_per_line - - avoid_null_checks_in_equality_operators - - avoid_positional_boolean_parameters - # - avoid_print - - avoid_private_typedef_functions - - avoid_redundant_argument_values - - avoid_relative_lib_imports - - avoid_renaming_method_parameters - - avoid_return_types_on_setters - - avoid_returning_null_for_void - - avoid_returning_this - - avoid_setters_without_getters - - avoid_shadowing_type_parameters - - avoid_single_cascade_in_expression_statements - # - avoid_slow_async_io - - avoid_type_to_string - # - avoid_types_as_parameter_names - # - avoid_types_on_closure_parameters - - avoid_unnecessary_containers - - avoid_unused_constructor_parameters - - avoid_void_async - - avoid_web_libraries_in_flutter - - await_only_futures - - camel_case_extensions - - camel_case_types - - cancel_subscriptions - - cascade_invocations - - cast_nullable_to_non_nullable - - close_sinks - - collection_methods_unrelated_type - - combinators_ordering - - comment_references - - conditional_uri_does_not_exist - - constant_identifier_names - - control_flow_in_finally - - curly_braces_in_flow_control_structures - - dangling_library_doc_comments - - depend_on_referenced_packages - - deprecated_consistency - - diagnostic_describe_all_properties - - directives_ordering - - discarded_futures - - do_not_use_environment - - empty_catches - - empty_constructor_bodies - - empty_statements - - eol_at_end_of_file - - exhaustive_cases - - file_names - - flutter_style_todos - - hash_and_equals - - implementation_imports - - implicit_call_tearoffs - - invalid_case_patterns - - join_return_with_assignment - - leading_newlines_in_multiline_strings - - library_annotations - - library_names - - library_prefixes - - library_private_types_in_public_api - # - lines_longer_than_80_chars - - literal_only_boolean_expressions - - missing_whitespace_between_adjacent_strings - - no_adjacent_strings_in_list - - no_default_cases - - no_duplicate_case_values - - no_leading_underscores_for_library_prefixes - - no_leading_underscores_for_local_identifiers - - no_logic_in_create_state - - no_runtimeType_toString - - non_constant_identifier_names - - noop_primitive_operations - - null_check_on_nullable_type_parameter - - null_closures - # - omit_local_variable_types - - one_member_abstracts - - only_throw_errors - - overridden_fields - - package_api_docs - - package_names - - package_prefixed_library_names - - parameter_assignments - - prefer_adjacent_string_concatenation - - prefer_asserts_in_initializer_lists - - prefer_asserts_with_message - - prefer_collection_literals - - prefer_conditional_assignment - - prefer_const_constructors - - prefer_const_constructors_in_immutables - - prefer_const_declarations - - prefer_const_literals_to_create_immutables - - prefer_constructors_over_static_methods - - prefer_contains - # - prefer_double_quotes - # - prefer_expression_function_bodies - - prefer_final_fields - - prefer_final_in_for_each - - prefer_final_locals - - prefer_final_parameters - - prefer_for_elements_to_map_fromIterable - - prefer_foreach - - prefer_function_declarations_over_variables - - prefer_generic_function_type_aliases - - prefer_if_elements_to_conditional_expressions - - prefer_if_null_operators - - prefer_initializing_formals - - prefer_inlined_adds - - prefer_int_literals - - prefer_interpolation_to_compose_strings - - prefer_is_empty - - prefer_is_not_empty - - prefer_is_not_operator - - prefer_iterable_whereType - - prefer_mixin - - prefer_null_aware_method_calls - - prefer_null_aware_operators - - prefer_relative_imports - - prefer_single_quotes - - prefer_spread_collections - - prefer_typing_uninitialized_variables - - prefer_void_to_null - - provide_deprecation_message - # - public_member_api_docs - - recursive_getters - - require_trailing_commas - - secure_pubspec_urls - - sized_box_for_whitespace - - sized_box_shrink_expand - - slash_for_doc_comments - - sort_child_properties_last - - sort_constructors_first - - sort_pub_dependencies - - sort_unnamed_constructors_first - - test_types_in_equals - - throw_in_finally - - tighten_type_of_initializing_formals - - type_annotate_public_apis - - type_init_formals - - unawaited_futures - - unnecessary_await_in_return - - unnecessary_brace_in_string_interps - - unnecessary_breaks - - unnecessary_const - - unnecessary_constructor_name - # - unnecessary_final - - unnecessary_getters_setters - - unnecessary_lambdas - - unnecessary_late - - unnecessary_library_directive - - unnecessary_new - - unnecessary_null_aware_assignments - - unnecessary_null_aware_operator_on_extension_on_nullable - - unnecessary_null_checks - - unnecessary_null_in_if_null_operators - - unnecessary_nullable_for_final_variable_declarations - - unnecessary_overrides - - unnecessary_parenthesis - - unnecessary_raw_strings - - unnecessary_statements - - unnecessary_string_escapes - - unnecessary_string_interpolations - - unnecessary_this - - unnecessary_to_list_in_spreads - - unreachable_from_main - - unrelated_type_equality_checks - - unsafe_html - - use_build_context_synchronously - - use_colored_box - - use_decorated_box - - use_enums - - use_full_hex_values_for_flutter_colors - - use_function_type_syntax_for_parameters - - use_if_null_to_convert_nulls_to_bools - - use_is_even_rather_than_modulo - - use_key_in_widget_constructors - - use_late_for_private_fields_and_variables - - use_named_constants - - use_raw_strings - - use_rethrow_when_possible - - use_setters_to_change_properties - - use_string_buffers - - use_string_in_part_of_directives - - use_super_parameters - - use_test_throws_matchers - - use_to_and_as_if_applicable - - valid_regexps - - void_checks diff --git a/dev/githubanalysis/bin/githubanalysis.dart b/dev/githubanalysis/bin/githubanalysis.dart deleted file mode 100644 index 7619db20f..000000000 --- a/dev/githubanalysis/bin/githubanalysis.dart +++ /dev/null @@ -1,11 +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:io' show exit; - -import 'package:githubanalysis/main.dart' as lib show main; - -void main(final List arguments) async { - exit(await lib.main(arguments)); -} diff --git a/dev/githubanalysis/lib/cache.dart b/dev/githubanalysis/lib/cache.dart deleted file mode 100644 index 5690800d1..000000000 --- a/dev/githubanalysis/lib/cache.dart +++ /dev/null @@ -1,104 +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 'dart:io'; - -import 'package:github/github.dart'; - -import 'utils.dart'; - -// Returns the data from the network, in the form used in the cache. -// -// It's a bit inefficient to have the data be serialized to string and then -// immediately reparsed, but it avoids any issues where the cache is interpreted -// differently than the original data. Since this code is not performance sensitive, -// the sanity is more important. -typedef PopulateCacheCallback = Future Function(); - -const String cacheSeparator = ':'; - -File cacheFileFor(final Directory cacheDirectory, final List key) { - for (final String k in key) { - verifyStringSanity(k, const {'\x00', '/', cacheSeparator}); - } - final String cacheName = key.join(cacheSeparator); - return File('${cacheDirectory.path}/$cacheName'); -} - -typedef Parser = T? Function(String); - -Future readFromFile(final File file, final Parser parser) async { - try { - return parser(await file.readAsString()); - } on FileSystemException { - if (await file.exists()) { - rethrow; - } - } - return null; -} - -Future loadFromCache( - final Directory cacheDirectory, - final GitHub github, - final List key, - final DateTime? cacheEpoch, - final PopulateCacheCallback callback, -) async { - final File cacheFile = cacheFileFor(cacheDirectory, key); - final RandomAccessFile cacheFileContents = await cacheFile.open(mode: FileMode.append); - bool firstWait = true; - while (true) { - try { - await cacheFileContents.lock(); - break; - } on FileSystemException catch (e) { - if (e.osError?.errorCode == 11) { - if (firstWait) { - print('\x1B[KWaiting for lock on ${cacheFile.path}'); - firstWait = false; - } - await Future.delayed(const Duration(seconds: 1)); - continue; - } - rethrow; - } - } - try { - await cacheFileContents.setPosition(0); - final String cacheData = utf8.decode(await cacheFileContents.read(await cacheFileContents.length())); - final int firstLineBreak = cacheData.indexOf('\n'); - bool needsReplacing = true; - if (cacheEpoch != null) { - if (firstLineBreak > 0) { - final int? cacheTimeInMilliseconds = int.tryParse(cacheData.substring(0, firstLineBreak), radix: 10); - if (cacheTimeInMilliseconds != null) { - final DateTime cacheTime = DateTime.fromMillisecondsSinceEpoch(cacheTimeInMilliseconds); - if (cacheTime.isAfter(cacheEpoch)) { - needsReplacing = false; - } - } - } - } - if (needsReplacing) { - final String data; - try { - data = await callback(); - } on Exception { - await cacheFile.delete(); - rethrow; - } - await cacheFileContents.truncate(0); - await cacheFileContents.setPosition(0); - await cacheFileContents.writeString('${DateTime.now().millisecondsSinceEpoch}\n'); - await cacheFileContents.writeString(data); - return data; - } - return cacheData.substring(firstLineBreak + 1); - } finally { - await cacheFileContents.unlock(); - await cacheFileContents.close(); - } -} diff --git a/dev/githubanalysis/lib/debug_http.dart b/dev/githubanalysis/lib/debug_http.dart deleted file mode 100644 index 10cf36cf8..000000000 --- a/dev/githubanalysis/lib/debug_http.dart +++ /dev/null @@ -1,39 +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 'dart:typed_data'; - -import 'package:http/http.dart'; - -class DebugHttpClient extends BaseClient { - DebugHttpClient({ - final Client? client, - }) : _client = client ?? Client(); - - final Client _client; - - @override - Future send(final BaseRequest request) async { - final StreamedResponse response = await _client.send(request); - final Uint8List bytes = await response.stream.toBytes(); - print(request.url); - print(utf8.decode(bytes)); - return StreamedResponse( - Stream>.value(bytes), - response.statusCode, - contentLength: response.contentLength, - request: response.request, - headers: response.headers, - isRedirect: response.isRedirect, - persistentConnection: response.persistentConnection, - reasonPhrase: response.reasonPhrase, - ); - } - - @override - void close() { - _client.close(); - } -} diff --git a/dev/githubanalysis/lib/issue.dart b/dev/githubanalysis/lib/issue.dart deleted file mode 100644 index 195f7f81c..000000000 --- a/dev/githubanalysis/lib/issue.dart +++ /dev/null @@ -1,337 +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 'dart:io'; -import 'dart:math' as math; - -import 'package:github/github.dart'; -import 'package:http/http.dart'; - -import 'cache.dart'; -import 'utils.dart'; - -const List priorities = ['P0', 'P1', 'P2', 'P3']; - -class FullIssue { - FullIssue( - this.repo, - this.issueNumber, - this._metadata, - this.comments, - this.reactions, { - this.redirect, - this.isDeleted = false, - }) { - _labels = _metadata?.labels.map((final IssueLabel label) => label.name).toSet(); - if (_labels != null) { - final Set matches = priorities.toSet().intersection(_labels!); - if (matches.length > 1) { - print('\nWARNING: Too many priority labels on issue #$issueNumber: ${matches.join(', ')}'); - for (final String priority in priorities) { - if (matches.contains(priority)) { - _priority = priority; - break; - } - } - } else if (matches.length == 1) { - _priority = matches.single; - } else { - _priority = null; - } - } - } - - final RepositorySlug repo; - final int issueNumber; - final Issue? _metadata; - Issue get metadata => _metadata!; // only available when isValid - final List comments; - final List reactions; - final String? redirect; // not null when metadata == null && !isDeleted - final bool isDeleted; - - late final Set? _labels; - Set get labels => _labels!; // only available when isValid - - late final String? _priority; - String? get priority => _priority; // only available when isValid - - bool get isValid => _metadata != null; - bool get isPullRequest => _metadata?.pullRequest != null; - - static Future load({ - required final Directory cache, - required final GitHub github, - required final RepositorySlug repo, - required final int issueNumber, - final DateTime? cacheEpoch, - }) async { - final String cacheData = await loadFromCache( - cache, github, ['issue', repo.owner, repo.name, issueNumber.toString()], cacheEpoch, () async { - try { - final Issue issue = await github.issues.get(repo, issueNumber); - if (issue.url.startsWith(github.endpoint)) { - if (issue.url != '${github.endpoint}/repos/$repo/issues/$issueNumber') { - return json.encode({'redirect': issue.url}); - } - } else if (issue.url == '') { - return json.encode({'deleted': true}); - } - final List comments = await github.issues.listCommentsByIssue(repo, issueNumber).toList(); - final List reactions = await github.issues.listReactions(repo, issueNumber).toList(); - return json.encode({'issue': issue, 'comments': comments, 'reactions': reactions}); - } on ClientException catch (e) { - // print('\nIssue $issueNumber is a problem child (treating as deleted): $e'); - return json.encode({'deleted': true, 'error': e.toString()}); - } - }); - final Map data = json.decode(cacheData)! as Map; - if (data['redirect'] != null) { - return FullIssue( - repo, - issueNumber, - null, - const [], - const [], - redirect: data['redirect']! as String, - ); - } - if (data['deleted'] == true) { - return FullIssue(repo, issueNumber, null, const [], const [], isDeleted: true); - } - final Issue issue = Issue.fromJson(data['issue']! as Map); - return FullIssue( - repo, - issueNumber, - issue, - (data['comments']! as List) - .cast>() - .map(IssueComment.fromJson) - .toList(), - (data['reactions']! as List).cast>().map(Reaction.fromJson).toList(), - ); - } - - @override - String toString() { - final StringBuffer result = StringBuffer(); - if (isValid) { - result - ..writeln('Issue $issueNumber (${metadata.state}): ${metadata.title}') - ..writeln(metadata.htmlUrl) - ..writeln( - 'Created by ${metadata.user!.login} on ${metadata.createdAt}, last updated on ${metadata.updatedAt}.', - ); - if (metadata.isClosed) { - result.writeln('Closed by ${metadata.closedBy?.login} on ${metadata.closedAt}.'); - if (metadata.closedBy != null) { - final User user = metadata.closedBy!; - result - ..writeln(' avatarUrl: ${user.avatarUrl}') - ..writeln(' bio: ${user.bio}') - ..writeln(' blog: ${user.blog}') - ..writeln(' company: ${user.company}') - ..writeln(' createdAt: ${user.createdAt}') - ..writeln(' email: ${user.email}') - ..writeln(' followersCount: ${user.followersCount}') - ..writeln(' followingCount: ${user.followingCount}') - ..writeln(' hirable: ${user.hirable}') - ..writeln(' htmlUrl: ${user.htmlUrl}') - ..writeln(' id: ${user.id}') - ..writeln(' location: ${user.location}') - ..writeln(' login: ${user.login}') - ..writeln(' name: ${user.name}') - ..writeln(' publicGistsCount: ${user.publicGistsCount}') - ..writeln(' publicReposCount: ${user.publicReposCount}') - ..writeln(' siteAdmin: ${user.siteAdmin}') - ..writeln(' twitterUsername: ${user.twitterUsername}') - ..writeln(' updatedAt: ${user.updatedAt}'); - } - } - if (isPullRequest) { - result.writeln(' Pull request: ${metadata.pullRequest?.diffUrl}'); - } - result - ..writeln('Assignees: ${metadata.assignees!.map((final User user) => user.login!).join(', ')}') - ..writeln('Labels: ${metadata.labels.map((final IssueLabel label) => label.name).join(', ')}') - ..writeln('Milestone: ${metadata.milestone ?? ""}'); - } else { - result.writeln('Issue $issueNumber (redirected): see $redirect'); - } - for (final IssueComment comment in comments) { - final String line = comment.body!.split('\n').first; - final String body = line.substring(0, math.min(40, line.length)); - result.writeln('Comment by ${comment.user!.login} at ${comment.createdAt}: $body'); - } - for (final Reaction reaction in reactions) { - result.writeln('Reaction: ${reaction.user!.login} ${reaction.content} at ${reaction.createdAt}'); - } - return result.toString(); - } -} - -Future fetchAllIssues( - final GitHub github, - final Directory cache, - final RepositorySlug repo, - final Duration issueMaxAge, - final Map results, -) async { - int index = 1; - int issues = 0; - int prs = 0; - int invalid = 0; - final File lastIssueNumberFile = cacheFileFor(cache, ['issue', repo.owner, repo.name, 'last']); - int lastIssueNumber = await readFromFile(lastIssueNumberFile, int.tryParse) ?? 0; - bool maxKnown = false; - while (true) { - try { - final FullIssue issue = await FullIssue.load( - cache: cache, - github: github, - repo: repo, - issueNumber: index, - cacheEpoch: maxAge(issueMaxAge), - ); - if (issue.isValid) { - if (issue.isPullRequest) { - prs += 1; - } else { - issues += 1; - } - } else if (issue.isDeleted) { - throw NotFound(github, 'Issue $index deleted or not yet filed.'); - } else { - // probably redirect - invalid += 1; - } - results[index] = issue; - } on FormatException catch (e) { - print('\nIssue $index could not be processed: $e'); - invalid += 1; - } on NotFound { - if (index > lastIssueNumber) { - final Issue lastIssue = - await github.issues.listByRepo(repo, state: 'all', sort: 'created', direction: 'desc').first; - lastIssueNumber = lastIssue.number; - maxKnown = true; - if (index > lastIssueNumber) { - break; - } - invalid += 1; - } - } - await rateLimit( - github, - '${repo.fullName}: #$index${lastIssueNumber > 0 ? " of ${maxKnown ? "" : "~"}$lastIssueNumber" : ""} ($issues issues; $prs PRs; $invalid errors)', - 'issue #${index + 1}', - ); - index += 1; - } - await lastIssueNumberFile.writeAsString(lastIssueNumber.toString()); - stdout.write('\x1B[K\r'); -} - -Future updateAllIssues( - final GitHub github, - final Directory cache, - final RepositorySlug repo, - final Map issues, -) async { - final Set pendingIssues = issues.isEmpty ? {} : issues.keys.toSet(); - int highestKnownIssue = pendingIssues.isEmpty ? 0 : (pendingIssues.toList()..sort()).last; - final File updateStampFile = cacheFileFor(cache, ['issue', repo.owner, repo.name, 'last-update']); - final DateTime? lastFullScanStartTime = await readFromFile(updateStampFile, DateTime.tryParse); - DateTime? thisFullScanStartTime; - int count = 0; - await rateLimit(github, '${repo.fullName}: fetching issues with recent changes', 'scan'); - await for (final Issue summary - in github.issues.listByRepo(repo, state: 'all', sort: 'updated', direction: 'desc', perPage: 100)) { - if (summary.updatedAt != null) { - thisFullScanStartTime ??= summary.updatedAt!; - } - if (mode == Mode.abbreviated && - summary.updatedAt != null && - lastFullScanStartTime != null && - summary.updatedAt!.isBefore(lastFullScanStartTime)) { - stdout.write('\x1B[K\r'); - return; - } - const int maxRetry = 5; - for (int retry = 1; retry <= maxRetry; retry += 1) { - try { - final String parenthetical; - Duration? delta = lastFullScanStartTime?.difference(summary.updatedAt!); - if (delta != null && delta.inMilliseconds < 0) { - delta = -delta; - if (delta.inDays > 2) { - parenthetical = '${delta.inDays} days remaining'; - } else if (delta.inHours > 2) { - parenthetical = '${delta.inHours} hours remaining'; - } else if (delta.inMinutes > 2) { - parenthetical = '${delta.inMinutes} minutes remaining'; - } else { - parenthetical = '${delta.inMilliseconds}ms remaining'; - } - } else { - parenthetical = '${100 * count ~/ (pendingIssues.length + count)}% newer than ${summary.updatedAt}'; - } - await rateLimit(github, '${repo.fullName}: $count issues updated ($parenthetical)', 'scan'); - final FullIssue issue = await FullIssue.load( - cache: cache, - github: github, - repo: repo, - issueNumber: summary.number, - cacheEpoch: summary.updatedAt, - ); - assert( - !issue.isValid || !issue.metadata.updatedAt!.isBefore(summary.updatedAt!), - 'invariant violation\nOLD DATA:\n${json.encode(issue.metadata.toJson())}\nNEW DATA:\n${json.encode(summary.toJson())}', - ); - if (issue.issueNumber > highestKnownIssue) { - for (int index = highestKnownIssue; index < issue.issueNumber; index += 1) { - pendingIssues.add(index); - } - highestKnownIssue = issue.issueNumber; - } else { - pendingIssues.remove(issue.issueNumber); - } - issues[issue.issueNumber] = issue; - break; - } on FormatException catch (e) { - print('\nError while updating issue #${summary.number} (attempt $retry/$maxRetry): $e'); - } - } - count += 1; - } - stdout.write('\x1B[K\r'); - if (pendingIssues.isNotEmpty) { - // looks like these went away, so force fetch them - int count = 0; - for (final int issueNumber in pendingIssues) { - await rateLimit( - github, - '${repo.fullName}: $count / ${pendingIssues.length} missing issues checked', - 'issue #$issueNumber', - ); - try { - issues[issueNumber] = await FullIssue.load( - cache: cache, - github: github, - repo: repo, - issueNumber: issueNumber, - cacheEpoch: lastFullScanStartTime, - ); - count += 1; - } on Exception catch (e) { - print('\nError while updating issue #$issueNumber: $e'); - } - } - stdout.write('\x1B[K\r'); - } - if (thisFullScanStartTime != null) { - await updateStampFile.writeAsString(thisFullScanStartTime.toIso8601String()); - } -} diff --git a/dev/githubanalysis/lib/main.dart b/dev/githubanalysis/lib/main.dart deleted file mode 100644 index e0a966508..000000000 --- a/dev/githubanalysis/lib/main.dart +++ /dev/null @@ -1,869 +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:async'; -import 'dart:io'; - -import 'package:github/github.dart'; -import 'package:http/http.dart'; - -import 'debug_http.dart'; -import 'issue.dart'; -import 'team.dart'; -import 'utils.dart'; - -const bool _debugNetwork = false; - -// File that contains OAuth token obtained via https://github.com/settings/personal-access-tokens/new -// + Resource owner "flutter" -// + Public Repositories (read-only) -// + Organization permissions -> Members -> Read-only -final File tokenFile = File('.github-token'); -final File membersFile = File('members.txt'); -final File exmembersFile = File('exmembers.txt'); - -final Directory cache = Directory('cache'); -final Directory output = Directory('output'); -const String orgName = 'flutter'; -final RepositorySlug issueDatabaseRepo = RepositorySlug(orgName, 'flutter'); -final Set repos = { - issueDatabaseRepo, - RepositorySlug(orgName, 'engine'), - RepositorySlug(orgName, 'buildroot'), - RepositorySlug(orgName, 'devtools'), - RepositorySlug(orgName, 'flutter-intellij'), - RepositorySlug(orgName, 'packages'), - RepositorySlug(orgName, 'codelabs'), - RepositorySlug(orgName, 'website'), - RepositorySlug(orgName, 'cocoon'), - RepositorySlug(orgName, 'platform_tests'), - RepositorySlug(orgName, 'samples'), - RepositorySlug(orgName, 'gallery'), - RepositorySlug(orgName, 'news_toolkit'), - RepositorySlug(orgName, 'holobooth'), - RepositorySlug(orgName, 'pinball'), - RepositorySlug(orgName, 'photobooth'), -}; -const String primaryTeam = 'flutter-hackers'; -const Duration rosterMaxAge = Duration(minutes: 10); -const Duration issueMaxAge = Duration(days: 365); - -const Set csvSpecials = {'\'', '"', ',', '\n'}; - -Future full(final Directory cache, final GitHub github) async { - try { - // FETCH USER AND TEAM DATA - print('Team roster...'); - final TeamRoster roster = await TeamRoster.load( - cache: cache, - github: github, - orgName: orgName, - cacheEpoch: maxAge(rosterMaxAge), - ); - final Set allMembers = {}; - final Set currentMembers = roster.teams[primaryTeam]!.keys.toSet(); - final Set expectedMembers = (await membersFile.readAsString()) - .trimRight() - .split('\n') - .where((final String name) => !name.endsWith(' (DO NOT ADD)')) - .toSet(); - final Set expectedExmembers = (await exmembersFile.readAsString()).trimRight().split('\n').toSet(); - try { - Set canon(final Set set) => set.map((final String s) => s.toLowerCase()).toSet(); - final Set unexpectedMembers = canon(currentMembers).difference(canon(expectedMembers)); - final Set memberExmembers = canon(expectedExmembers).intersection(canon(currentMembers)); - final Set missingMembers = canon(expectedMembers).difference(canon(currentMembers)); - if (unexpectedMembers.isNotEmpty) { - print( - 'WARNING: The following users are currently members of $primaryTeam but not expected: ${unexpectedMembers.join(', ')}', - ); - } - if (memberExmembers.isNotEmpty) { - print( - 'WARNING: The following users are currently members of $primaryTeam but should have been removed: ${memberExmembers.join(', ')}', - ); - } - if (missingMembers.isNotEmpty) { - print( - 'WARNING: The following users are currently NOT members of $primaryTeam but were expected:\n ${missingMembers.join('\n ')}', - ); - } - allMembers - ..addAll(currentMembers) - ..addAll(expectedMembers) - ..addAll(expectedExmembers); - } on FileSystemException catch (e) { - if (membersFile.existsSync()) { - print('Unable to read ${membersFile.path}: ${e.message}'); - return 1; - } - } - for (final String? teamName in roster.teams.keys.where((final String? team) => team != null)) { - for (final String userName in roster.teams[teamName]!.keys) { - if (!roster.teams[null]!.containsKey(userName)) { - print('WARNING: user $userName is in $teamName but not in organization.'); - } - } - } - - // FETCH ACTIVITY - print(''); - print('Fetching issues...'); - final Map> issues = >{}; - try { - for (final RepositorySlug repo in repos) { - await fetchAllIssues(github, cache, repo, issueMaxAge, issues[repo.fullName] = {}); - } - print('Updating issues...'); - for (final RepositorySlug repo in repos) { - await updateAllIssues(github, cache, repo, issues[repo.fullName]!); - } - } on Abort {} // ignore: empty_catches - - // ANALYZE ACTIVITY RESULTS - print(''); - print('Analyzing...'); - try { - await output.create(recursive: true); - } on FileSystemException catch (e) { - print('Unable to create output in "${output.path}": $e'); - return 1; - } - final Map activityMetrics = {}; - UserActivity forUser(final User? user) { - return activityMetrics.putIfAbsent(user!.login!, () { - final UserActivity result = UserActivity(); - if (expectedMembers.contains(user.login)) { - result - ..isMember = true - ..isActiveMember = true; - } else if (expectedExmembers.contains(user.login)) { - result.isMember = true; - } - return result; - }); - } - - final Set reactionKinds = {}; - final Set foundPriorities = {}; - void increment(final Map map, final Set keys, final T key) { - keys.add(key); - if (map.containsKey(key)) { - map[key] = map[key]! + 1; - } else { - map[key] = 1; - } - } - - void processText(final UserActivity activity, final String body) { - activity.characters += body.length; - } - - for (final String user in currentMembers) { - forUser(User(login: user)); - } - final List allIssues = issues.values - .expand((final Map issues) => issues.values) - .where((final FullIssue issue) => issue.isValid) - .toList(); - for (final FullIssue issue in allIssues) { - if (issue.isPullRequest) { - forUser(issue.metadata.user).pullRequests.add(issue.metadata.createdAt); - } else { - forUser(issue.metadata.user).issues.add(issue.metadata.createdAt); - increment(forUser(issue.metadata.user).priorityCount, foundPriorities, issue.priority); - } - if (!issue.isPullRequest && (issue.metadata.closedBy != null)) { - forUser(issue.metadata.closedBy).closures.add(issue.metadata.closedAt); - if (issue.metadata.closedBy!.login == issue.metadata.user!.login) { - forUser(issue.metadata.closedBy).selfClosures += 1; - } - } - processText(forUser(issue.metadata.user), issue.metadata.body); - for (final IssueComment comment in issue.comments) { - forUser(comment.user).comments.add(comment.createdAt); - processText(forUser(comment.user), comment.body!); - } - for (final Reaction reaction in issue.reactions) { - forUser(reaction.user).reactions.add(reaction.createdAt); - increment(forUser(reaction.user).reactionCount, reactionKinds, reaction.content!); - } - } - DateTime? earliest; - DateTime? latest; - void considerTimes(final UserActivity activity, final List times) { - for (final DateTime? time in times) { - if (activity.earliest == null || (time != null && time.isBefore(activity.earliest!))) { - activity.earliest = time; - } - if (activity.latest == null || (time != null && time.isAfter(activity.latest!))) { - activity.latest = time; - } - if (earliest == null || (time != null && time.isBefore(earliest!))) { - earliest = time; - } - if (latest == null || (time != null && time.isAfter(latest!))) { - latest = time; - } - } - } - - for (final UserActivity activity in activityMetrics.values) { - considerTimes(activity, activity.issues); - considerTimes(activity, activity.comments); - considerTimes(activity, activity.closures); - considerTimes(activity, activity.pullRequests); - considerTimes(activity, activity.reactions); - } - - // PRINT ACTIVITY RESULTS - final StringBuffer summary = StringBuffer(); - for (final String reactionKind in reactionKinds) { - verifyStringSanity(reactionKind, csvSpecials); - } - final List sortedReactionKinds = reactionKinds.toList()..sort(); - summary.writeln( - 'user,is member,is active member,earliest,latest,days active,total,density,issues,comments,closures,self closures,pull requests,characters,missing priority,${priorities.join(',')},reactions,${sortedReactionKinds.join(',')}', - ); - int usersWithMoreThanOneDayActive = 0; - for (final String user in activityMetrics.keys.toList() - ..sort((final String a, final String b) => activityMetrics[b]!.total - activityMetrics[a]!.total)) { - verifyStringSanity(user, csvSpecials); - final UserActivity activity = activityMetrics[user]!; - if (activity.daysActive > 0) { - usersWithMoreThanOneDayActive += 1; - } - summary.write( - '$user,${activity.isMember},${activity.isActiveMember},${activity.earliest},${activity.latest},${activity.daysActive},${activity.total},${activity.density},${activity.issues.length},${activity.comments.length},${activity.closures.length},${activity.selfClosures},${activity.pullRequests.length},${activity.characters},${activity.priorityCount[null] ?? 0}', - ); - for (final String priority in priorities) { - summary.write(',${activity.priorityCount[priority] ?? 0}'); - } - summary.write(',${activity.reactions.length}'); - for (final String reactionKind in sortedReactionKinds) { - summary.write(',${activity.reactionCount[reactionKind] ?? 0}'); - } - summary.writeln(); - } - await File('${output.path}/users.csv').writeAsString(summary.toString()); - print('Total participants: ${activityMetrics.length}'); - print('Participants with more than one day of activity: $usersWithMoreThanOneDayActive'); - print('User activity results stored in: ${output.path}/users.csv'); - - // ANALYZE PRIORITIES - final Map priorityAnalysis = {}; - for (final String priority in priorities) { - priorityAnalysis[priority] = PriorityResults(); - } - final List primaryIssues = issues[issueDatabaseRepo.fullName]! - .values - .where((final FullIssue issue) => issue.isValid && !issue.isPullRequest) - .toList(); - final List primaryPRs = issues[issueDatabaseRepo.fullName]! - .values - .where((final FullIssue issue) => issue.isValid && issue.isPullRequest) - .toList(); - for (final FullIssue issue in primaryIssues.where((final FullIssue issue) => issue.priority != null)) { - final PriorityResults priorityResults = priorityAnalysis[issue.priority!]!; - final bool teamIssue = allMembers.contains(issue.metadata.user!.login); - priorityResults.total += 1; - if (teamIssue) { - priorityResults.openedByTeam += 1; - } else { - priorityResults.openedByNonTeam += 1; - } - if (issue.metadata.isOpen) { - priorityResults.open += 1; - } else { - priorityResults.closed += 1; - if (issue.metadata.closedAt == null || issue.metadata.createdAt == null) { - print( - 'WARNING: bogus open/close timeline data in ${issue.issueNumber}: opened at ${issue.metadata.createdAt}, closed at ${issue.metadata.closedAt}', - ); - } else { - final Duration timeOpen = issue.metadata.closedAt!.difference(issue.metadata.createdAt!); - priorityResults.timeOpen.add(timeOpen); - } - if (teamIssue) { - priorityResults.openedByTeamAndClosed += 1; - } else { - priorityResults.openedByNonTeamAndClosed += 1; - } - } - } - - // PRINT PRIORITY RESULTS - summary - ..clear() - ..writeln( - 'priority,total,open,closed,openedByTeam,openedByNonTeam,openedByTeamAndClosed,openedByNonTeamAndClosed,meanTimeOpen,p01TimeOpen,p05TimeOpen,medianTimeOpen,p95TimeOpen,p99TimeOpen', - ); - for (final String priority in priorities) { - verifyStringSanity(priority, csvSpecials); - final PriorityResults entry = priorityAnalysis[priority]!; - summary.write( - '$priority,${entry.total},${entry.open},${entry.closed},${entry.openedByTeam},${entry.openedByNonTeam},${entry.openedByTeamAndClosed},${entry.openedByNonTeamAndClosed},', - ); - if (entry.timeOpen.isEmpty) { - summary.write('NaN,NaN,NaN,NaN'); - } else { - entry.timeOpen.sort(); - summary - ..write( - '${entry.timeOpen.fold(0, (final int sum, final Duration t) => sum + t.inMilliseconds) / (entry.timeOpen.length * Duration.millisecondsPerDay)},', - ) - ..write( - '${entry.timeOpen[(entry.timeOpen.length * 0.01).floor()].inMilliseconds / Duration.millisecondsPerDay},', - ) - ..write( - '${entry.timeOpen[(entry.timeOpen.length * 0.05).floor()].inMilliseconds / Duration.millisecondsPerDay},', - ); - if (entry.timeOpen.length > 1) { - final Duration median1 = entry.timeOpen[(entry.timeOpen.length / 2.0).floor()]; - final Duration median2 = entry.timeOpen[(entry.timeOpen.length / 2.0).ceil()]; - summary.write('${(median1.inMilliseconds + median2.inMilliseconds) / (2.0 * Duration.millisecondsPerDay)},'); - } else { - summary.write('${(entry.timeOpen.first.inMilliseconds) / Duration.millisecondsPerDay},'); - } - summary - ..write( - '${entry.timeOpen[(entry.timeOpen.length * 0.95).floor()].inMilliseconds / Duration.millisecondsPerDay},', - ) - ..write( - '${entry.timeOpen[(entry.timeOpen.length * 0.99).floor()].inMilliseconds / Duration.millisecondsPerDay},', - ); - } - summary.writeln(); - } - await File('${output.path}/priorities.csv').writeAsString(summary.toString()); - print('Priority results stored in: ${output.path}/priorities.csv'); - - // PRINT ISSUE DATA - int deadCount = 0; - int zombieCount = 0; - summary - ..clear() - ..writeln( - 'repository,issue,state,createdAt,createdBy,closedAt,closedBy,timeOpen,updatedAt,priority,labelCount,commentCount,${sortedReactionKinds.join(',')},daysToTwentyVotes,isNewFeature,isProposal,isPendingAutoclosure,isFiledByTeam,isFiledByExMember,', - ); - for (final FullIssue issue in allIssues.where((final FullIssue issue) => !issue.isPullRequest)) { - verifyStringSanity(issue.metadata.state, csvSpecials); - summary.write( - '${issue.repo.fullName},${issue.issueNumber},${issue.metadata.state},${issue.metadata.createdAt},${issue.metadata.user!.login},${issue.metadata.closedAt},${issue.metadata.closedBy?.login ?? (issue.isValid && issue.metadata.isClosed ? "" : "")},${issue.isValid && issue.metadata.isClosed ? issue.metadata.closedAt!.difference(issue.metadata.createdAt!).inMilliseconds / Duration.millisecondsPerDay : ""},${issue.metadata.updatedAt},${issue.priority ?? ""},${issue.labels.length},${issue.comments.length}', - ); - for (final String reactionKind in sortedReactionKinds) { - int count = 0; - for (final Reaction reaction in issue.reactions) { - if (reaction.content == reactionKind) { - count += 1; - } - } - summary.write(',$count'); - } - int count = 0; - int? daysToTwentyVotes; - for (final Reaction reaction in issue.reactions) { - if (reaction.content == '+1') { - count += 1; - if (count >= 20) { - daysToTwentyVotes = reaction.createdAt!.difference(issue.metadata.createdAt!).inDays; - break; - } - } - } - summary - ..write(',${daysToTwentyVotes ?? ''}') - ..write(',${issue.labels.contains('new feature')}') - ..write(',${issue.labels.contains('proposal')}') - ..write(',${issue.labels.contains('waiting for customer response')}') - ..write(',${allMembers.contains(issue.metadata.user!.login)}') - ..write(',${expectedExmembers.contains(issue.metadata.user!.login)}') - ..writeln(); - if ((daysToTwentyVotes == null || daysToTwentyVotes > 60) && - issue.labels.contains('new feature') && - (issue.metadata.isOpen || issue.metadata.closedAt!.difference(issue.metadata.createdAt!).inDays > 60)) { - if (issue.metadata.isOpen) { - deadCount += 1; - } else { - zombieCount += 1; - } - } - } - await File('${output.path}/issues.csv').writeAsString(summary.toString()); - print('Issue summaries stored in: ${output.path}/issues.csv'); - print('$deadCount issues would be closed; $zombieCount issues would not have been fixed.'); - - // COLLECT CLOSE TIME PERCENTILES - int maxDaysToClose = 0; - final Map> closureTimeHistogramClosed = >{}; - final Map closureTimeTotalsClosed = { - null: 0, - for (final String priority in priorities) priority: 0, - }; - for (final FullIssue issue in primaryIssues.where((final FullIssue issue) => issue.metadata.isClosed)) { - final int timeOpen = issue.metadata.closedAt!.difference(issue.metadata.createdAt!).inDays; - final String? priority = issue.priority; - closureTimeHistogramClosed.putIfAbsent(timeOpen, () => {}).update( - priority, - (final int value) => value + 1, - ifAbsent: () => 1, - ); - closureTimeTotalsClosed[priority] = closureTimeTotalsClosed[priority]! + 1; - if (timeOpen > maxDaysToClose) { - maxDaysToClose = timeOpen; - } - } - - // PRINT CLOSE TIME PERCENTILES OF CLOSED BUGS - summary - ..clear() - ..writeln( - 'time to close (days),unprioritized,${priorities.where((final String priority) => closureTimeTotalsClosed[priority]! > 0).join(",")},unprioritized,${priorities.where((final String priority) => closureTimeTotalsClosed[priority]! > 0).join(",")}', - ); - if (closureTimeTotalsClosed[null]! > 0) { - final Map closureTimeCumulativeSum = { - null: 0, - for (final String priority in priorities) - if (closureTimeTotalsClosed[priority]! > 0) priority: 0, - }; - for (int day = 0; day <= maxDaysToClose; day += 1) { - if (closureTimeHistogramClosed.containsKey(day)) { - if (closureTimeHistogramClosed[day]!.containsKey(null)) { - closureTimeCumulativeSum[null] = closureTimeCumulativeSum[null]! + closureTimeHistogramClosed[day]![null]!; - } - for (final String priority in priorities) { - if (closureTimeHistogramClosed[day]!.containsKey(priority)) { - closureTimeCumulativeSum[priority] = - closureTimeCumulativeSum[priority]! + closureTimeHistogramClosed[day]![priority]!; - } - } - } - summary.write('$day,${closureTimeCumulativeSum[null]}'); - for (final String priority in priorities) { - if (closureTimeTotalsClosed[priority]! > 0) { - summary.write(',${closureTimeCumulativeSum[priority]}'); - } - } - summary.write(',${100.0 * closureTimeCumulativeSum[null]! / closureTimeTotalsClosed[null]!}%'); - for (final String priority in priorities) { - if (closureTimeTotalsClosed[priority]! > 0) { - summary.write(',${100.0 * closureTimeCumulativeSum[priority]! / closureTimeTotalsClosed[priority]!}%'); - } - } - summary.writeln(); - } - } - await File('${output.path}/priority-percentiles.csv').writeAsString(summary.toString()); - print('Priority percentiles stored in: ${output.path}/priority-percentiles.csv'); - - // COLLECT CLOSE TIME PERCENTILES OF ALL BUGS - final Map> closureTimeHistogramAll = >{}; - final Map closureTimeTotalsAll = { - null: 0, - for (final String priority in priorities) priority: 0, - }; - for (final FullIssue issue in primaryIssues) { - final String? priority = issue.priority; - closureTimeTotalsAll[priority] = closureTimeTotalsAll[priority]! + 1; - final int timeOpen = issue.metadata.isClosed - ? issue.metadata.closedAt!.difference(issue.metadata.createdAt!).inDays - : maxDaysToClose + 1; - closureTimeHistogramAll.putIfAbsent(timeOpen, () => {}).update( - priority, - (final int value) => value + 1, - ifAbsent: () => 1, - ); - } - - // PRINT CLOSE TIME PERCENTILES OF ALL BUGS - summary - ..clear() - ..writeln( - 'time to close (days),unprioritized,${priorities.where((final String priority) => closureTimeTotalsAll[priority]! > 0).join(",")},unprioritized,${priorities.where((final String priority) => closureTimeTotalsAll[priority]! > 0).join(",")}', - ); - if (closureTimeTotalsAll[null]! > 0) { - final Map closureTimeCumulativeSum = { - null: 0, - for (final String priority in priorities) - if (closureTimeTotalsAll[priority]! > 0) priority: 0, - }; - for (int day = 0; day <= maxDaysToClose + 1; day += 1) { - if (closureTimeHistogramAll.containsKey(day)) { - if (closureTimeHistogramAll[day]!.containsKey(null)) { - closureTimeCumulativeSum[null] = closureTimeCumulativeSum[null]! + closureTimeHistogramAll[day]![null]!; - } - for (final String priority in priorities) { - if (closureTimeHistogramAll[day]!.containsKey(priority)) { - closureTimeCumulativeSum[priority] = - closureTimeCumulativeSum[priority]! + closureTimeHistogramAll[day]![priority]!; - } - } - } - summary.write('$day,${closureTimeCumulativeSum[null]}'); - for (final String priority in priorities) { - if (closureTimeTotalsAll[priority]! > 0) { - summary.write(',${closureTimeCumulativeSum[priority]}'); - } - } - summary.write(',${100.0 * closureTimeCumulativeSum[null]! / closureTimeTotalsAll[null]!}%'); - for (final String priority in priorities) { - if (closureTimeTotalsAll[priority]! > 0) { - summary.write(',${100.0 * closureTimeCumulativeSum[priority]! / closureTimeTotalsAll[priority]!}%'); - } - } - summary.writeln(); - } - } - await File('${output.path}/priority-percentiles-all.csv').writeAsString(summary.toString()); - print('Priority percentiles stored in: ${output.path}/priority-percentiles-all.csv'); - - // PRINT PR DATA - summary - ..clear() - ..writeln( - 'repository,pr,user,state,createdAt,closedAt,timeOpen,updatedAt,labelCount,commentCount,${sortedReactionKinds.join(',')}', - ); - for (final FullIssue issue in allIssues.where((final FullIssue issue) => issue.isPullRequest)) { - verifyStringSanity(issue.metadata.state, csvSpecials); - summary.write( - '${issue.repo.fullName},${issue.issueNumber},${issue.metadata.user!.login},${issue.metadata.state},${issue.metadata.createdAt},${issue.metadata.closedAt},${issue.metadata.isClosed ? issue.metadata.closedAt!.difference(issue.metadata.createdAt!).inMilliseconds / Duration.millisecondsPerDay : ""},${issue.metadata.updatedAt},${issue.labels.length},${issue.comments.length}', - ); - for (final String reactionKind in sortedReactionKinds) { - int count = 0; - for (final Reaction reaction in issue.reactions) { - if (reaction.content == reactionKind) { - count += 1; - } - } - summary.write(',$count'); - } - summary.writeln(); - } - await File('${output.path}/prs.csv').writeAsString(summary.toString()); - print('PR summaries stored in: ${output.path}/prs.csv'); - - // PRINT USERS - final List teamNames = roster.teams.keys.where((final String? name) => name != null).cast().toList() - ..sort(); - final List userNames = roster.teams[null]!.keys.toList()..sort(); - summary - ..clear() - ..writeln('user,${teamNames.join(',')}'); - for (final String userName in userNames) { - verifyStringSanity(userName, csvSpecials); - summary.write(userName); - for (final String? teamName in teamNames) { - summary.write(','); - if (roster.teams[teamName]!.containsKey(userName)) { - summary.write('1'); - } else { - summary.write('0'); - } - } - summary.writeln(); - } - await File('${output.path}/teams.csv').writeAsString(summary.toString()); - print('Team membership summaries stored in: ${output.path}/teams.csv'); - - // WEEKLY ACTIVITY OVER TIME - if (earliest != null) { - assert(latest != null, 'invariant violation'); - const int window = Duration.millisecondsPerDay * 7; - final int firstWeekStart = earliest!.millisecondsSinceEpoch ~/ window; - final List weeks = List.generate( - 1 + (latest!.millisecondsSinceEpoch ~/ window) - firstWeekStart, - (final int index) => WeekActivity( - DateTime.fromMillisecondsSinceEpoch((index + firstWeekStart) * window), - reactionKinds, - priorities, - ), - ); - WeekActivity? forWeek(final DateTime? time) { - if (time == null) { - return null; - } - return weeks[(time.millisecondsSinceEpoch ~/ window) - firstWeekStart]; - } - - for (final FullIssue issue in allIssues) { - if (!issue.isValid) { - continue; - } - if (issue.isPullRequest) { - forWeek(issue.metadata.createdAt)!.pullRequests += 1; - } else { - forWeek(issue.metadata.createdAt)!.issues += 1; - forWeek(issue.metadata.createdAt)!.priorityCount[issue.priority] = - forWeek(issue.metadata.createdAt)!.priorityCount[issue.priority]! + 1; - if (issue.metadata.isOpen) { - forWeek(issue.metadata.createdAt)!.remainingIssues += 1; - } - } - if (!issue.isPullRequest && (issue.metadata.closedBy != null)) { - forWeek(issue.metadata.closedAt)?.closures += 1; - if (issue.metadata.closedBy!.login == issue.metadata.user!.login) { - forWeek(issue.metadata.closedAt)?.selfClosures += 1; - } - } - forWeek(issue.metadata.createdAt)?.characters += issue.metadata.body.length; - for (final IssueComment comment in issue.comments) { - forWeek(comment.createdAt)!.comments += 1; - forWeek(comment.createdAt)!.characters += comment.body!.length; - } - for (final Reaction reaction in issue.reactions) { - forWeek(reaction.createdAt)!.reactions += 1; - forWeek(reaction.createdAt)!.reactionCount[reaction.content!] = - forWeek(reaction.createdAt)!.reactionCount[reaction.content!]! + 1; - } - } - if (weeks.isNotEmpty) { - weeks.removeLast(); // last week is incomplete data - } - - // PRINT WEEKLY ACTIVITY - summary - ..clear() - ..writeln( - 'week,total,issues,remaining issues,closures,self closures,net issues opened,comments,pull requests,characters,missing priority,${priorities.join(',')},reactions,${sortedReactionKinds.join(',')}', - ); - for (final WeekActivity week in weeks) { - verifyStringSanity(week.start.toIso8601String(), csvSpecials); - summary.write( - '${week.start},${week.total},${week.issues},${week.remainingIssues},${week.closures},${week.selfClosures},${week.issues - week.closures},${week.comments},${week.pullRequests},${week.characters},${week.priorityCount[null]!}', - ); - for (final String priority in priorities) { - summary.write(',${week.priorityCount[priority]!}'); - } - summary.write(',${week.reactions}'); - for (final String reactionKind in sortedReactionKinds) { - summary.write(',${week.reactionCount[reactionKind]!}'); - } - summary.writeln(); - } - await File('${output.path}/weeks.csv').writeAsString(summary.toString()); - print('Weekly activity results stored in: ${output.path}/weeks.csv'); - } - - // COLLECT LABELS DATA - final Map labels = {}; - final DateTime now = DateTime.now(); - for (final FullIssue issue in primaryIssues) { - for (final IssueLabel label in issue.metadata.labels) { - final LabelData data = labels.putIfAbsent(label.name, () => LabelData(label.name)) - ..all += 1 - ..issues += 1; - if (issue.metadata.isOpen) { - data.open += 1; - } - if (issue.metadata.isClosed) { - data.closed += 1; - } - if (now.difference(issue.metadata.updatedAt!) < const Duration(days: 52 * 7)) { - data.issuesUpdated52 += 1; - if (now.difference(issue.metadata.updatedAt!) < const Duration(days: 12 * 7)) { - data.issuesUpdated12 += 1; - } - } - } - } - for (final FullIssue issue in primaryPRs) { - for (final IssueLabel label in issue.metadata.labels) { - final LabelData data = labels.putIfAbsent(label.name, () => LabelData(label.name)) - ..all += 1 - ..prs += 1; - if (now.difference(issue.metadata.updatedAt!) < const Duration(days: 52 * 7)) { - data.prsUpdated52 += 1; - if (now.difference(issue.metadata.updatedAt!) < const Duration(days: 12 * 7)) { - data.prsUpdated12 += 1; - } - } - } - } - - // PRINT LABELS DATA - summary - ..clear() - ..writeln( - 'label,issues and PRs,all issues,open issues,closed issues,issues updated in last 12 weeks,issues updated in last 52 weeks,all PRs,PRs updated in last 12 weeks,PRs updated in last 52 weeks', - ); - for (final LabelData label in labels.values) { - verifyStringSanity(label.name, csvSpecials); - summary.writeln( - '${label.name},${label.all},${label.issues},${label.open},${label.closed},${label.issuesUpdated12},${label.issuesUpdated52},${label.prs},${label.prsUpdated12},${label.prsUpdated52}', - ); - } - await File('${output.path}/labels.csv').writeAsString(summary.toString()); - print('Labels stored in: ${output.path}/labels.csv'); - - return 0; - } on Abort { - print(''); - return 2; - // ignore: avoid_catches_without_on_clauses - } catch (e, stack) { - print('\nFatal error (${e.runtimeType}).'); - print('$e\n$stack'); - return 1; - } -} - -class LabelData { - LabelData(this.name); - final String name; - int all = 0; - int issues = 0; - int open = 0; - int closed = 0; - int issuesUpdated12 = 0; - int issuesUpdated52 = 0; - int prs = 0; - int prsUpdated12 = 0; - int prsUpdated52 = 0; -} - -class WeekActivity { - WeekActivity(this.start, final Set reactionKinds, final List priorities) { - priorityCount[null] = 0; - for (final String priority in priorities) { - priorityCount[priority] = 0; - } - for (final String reactionKind in reactionKinds) { - reactionCount[reactionKind] = 0; - } - } - final DateTime start; - int issues = 0; - int comments = 0; - int closures = 0; - int remainingIssues = 0; - int pullRequests = 0; - int reactions = 0; - Map reactionCount = {}; - Map priorityCount = {}; - int selfClosures = 0; - int characters = 0; - - int get total => issues + comments + closures + pullRequests + reactions; -} - -class UserActivity { - bool isMember = false; - bool isActiveMember = false; - List issues = []; - List comments = []; - List closures = []; - List pullRequests = []; - List reactions = []; - Map reactionCount = {}; - Map priorityCount = {}; - int selfClosures = 0; - int characters = 0; - - DateTime? earliest; - DateTime? latest; - - int get total => issues.length + comments.length + closures.length + pullRequests.length + reactions.length; - double get density => (earliest == null || latest == null) - ? double.nan - : total / (latest!.millisecondsSinceEpoch - earliest!.millisecondsSinceEpoch); - double get daysActive => (earliest == null || latest == null) - ? double.nan - : (latest!.millisecondsSinceEpoch - earliest!.millisecondsSinceEpoch) / Duration.millisecondsPerDay; -} - -class PriorityResults { - int total = 0; - int open = 0; - int closed = 0; - int openedByTeam = 0; - int openedByNonTeam = 0; - int openedByTeamAndClosed = 0; - int openedByNonTeamAndClosed = 0; - final List timeOpen = []; -} - -Future main(final List arguments) async { - print(''); - print('GitHub Repository Analysis'); - print('=========================='); - print(''); - ProcessSignal.sigint.watch().listen((final ProcessSignal signal) { - stdout.write('\x1B[K\r'); - switch (mode) { - case Mode.full: - print('Skipping full update...'); - mode = Mode.abbreviated; - case Mode.abbreviated: - mode = Mode.aborted; - print('Skipping to generation...'); - aborter.complete(); - case Mode.aborted: - print('Terminating immediately!'); - exit(2); - } - }); - try { - await cache.create(recursive: true); - } on FileSystemException catch (e) { - print('Unable to create cache in "${cache.path}": $e'); - return 1; - } - final Client client = _debugNetwork ? DebugHttpClient() : Client(); - late final GitHub github; - try { - final String token = await tokenFile.readAsString(); - github = GitHub(auth: Authentication.withToken(token), client: client); - } on FileSystemException catch (e) { - if (tokenFile.existsSync()) { - print('Unable to read ${tokenFile.path}: ${e.message}'); - return 1; - } - print('No token file; connecting to GitHub anonymously...'); - print(''); - github = GitHub(client: client); - } - if (arguments.isEmpty) { - return full(cache, github); - } - for (final String argument in arguments) { - final List parts = argument.split(':'); - if (parts.isNotEmpty && parts[0] == 'issue') { - if (parts.length != 4) { - print('Not sure what to do with "$argument" (format for issue is issue:org:repo:number).'); - exit(1); - } - final RepositorySlug repo = RepositorySlug(parts[1], parts[2]); - final int? issueNumber = int.tryParse(parts[3], radix: 10); - if (issueNumber == null) { - print('Not sure what to do with "$argument" (fourth component is not a number).'); - exit(1); - } - final FullIssue issue = await FullIssue.load( - cache: cache, - github: github, - repo: repo, - issueNumber: issueNumber, - cacheEpoch: DateTime.now().subtract(const Duration(hours: 24)), - ); - final StringBuffer summary = StringBuffer(); - final List thumbs = - List.filled(issue.reactions.last.createdAt!.difference(issue.metadata.createdAt!).inDays + 1, 0); - for (final Reaction reaction in issue.reactions) { - if (reaction.content == '+1') { - final int day = reaction.createdAt!.difference(issue.metadata.createdAt!).inDays; - thumbs[day] = thumbs[day] + 1; - } - } - summary.writeln('day,thumbs,sum'); - int sum = 0; - for (int day = 0; day < thumbs.length; day += 1) { - sum += thumbs[day]; - summary.writeln('$day,${thumbs[day]},$sum'); - } - await File('${output.path}/issue:${repo.owner}:${repo.name}:$issueNumber:thumbs:history.csv') - .writeAsString(summary.toString()); - } - } - return 0; -} diff --git a/dev/githubanalysis/lib/team.dart b/dev/githubanalysis/lib/team.dart deleted file mode 100644 index f0213f961..000000000 --- a/dev/githubanalysis/lib/team.dart +++ /dev/null @@ -1,81 +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:io'; - -import 'package:github/github.dart'; - -import 'cache.dart'; -import 'utils.dart'; - -class TeamRoster { - TeamRoster(this.teams); - - static Future load({ - required final Directory cache, - required final GitHub github, - required final String orgName, - required final DateTime cacheEpoch, - }) async { - final Map> roster = >{}; - final String teamsData = - await loadFromCache(cache, github, ['org', orgName, 'teams'], cacheEpoch, () async { - final StringBuffer cacheData = StringBuffer(); - await for (final Team team in github.organizations.listTeams(orgName)) { - verifyStringSanity(team.name!, const {'\n', ' '}); - cacheData.writeln('${team.name} ${team.id!}'); - } - return cacheData.toString().trimRight(); - }); - Map parseTeamData(final String teamData) { - final Map users = {}; - for (final String line in teamData.split('\n')) { - if (line.isNotEmpty) { - final List components = line.split(' '); - final User member = User( - login: components[0], - id: int.parse(components[1]), - siteAdmin: components[2] == 'true', - htmlUrl: components[3], - avatarUrl: components[4], - ); - users[member.login!] = member; - } - } - return users; - } - - for (final String teamLine in teamsData.split('\n')) { - final List components = teamLine.split(' '); - final String teamName = components[0]; - final int teamId = int.parse(components[1]); - final String teamData = - await loadFromCache(cache, github, ['team', orgName, '$teamId'], cacheEpoch, () async { - final StringBuffer cacheData = StringBuffer(); - await for (final TeamMember member in github.organizations.listTeamMembers(teamId)) { - verifyStringSanity(member.login!, const {'\n', ' '}); - verifyStringSanity(member.htmlUrl!, const {'\n', ' '}); - verifyStringSanity(member.avatarUrl!, const {'\n', ' '}); - cacheData.writeln('${member.login} ${member.id!} ${member.siteAdmin} ${member.htmlUrl} ${member.avatarUrl}'); - } - return cacheData.toString().trimRight(); - }); - roster[teamName] = parseTeamData(teamData); - } - final String teamData = await loadFromCache(cache, github, ['org', orgName, 'users'], cacheEpoch, () async { - final StringBuffer cacheData = StringBuffer(); - await for (final User member in github.organizations.listUsers(orgName)) { - verifyStringSanity(member.login!, const {'\n', ' '}); - verifyStringSanity(member.htmlUrl!, const {'\n', ' '}); - verifyStringSanity(member.avatarUrl!, const {'\n', ' '}); - cacheData.writeln('${member.login} ${member.id!} ${member.siteAdmin} ${member.htmlUrl} ${member.avatarUrl}'); - } - return cacheData.toString().trimRight(); - }); - roster[null] = parseTeamData(teamData); - return TeamRoster(roster); - } - - final Map> teams; -} diff --git a/dev/githubanalysis/lib/utils.dart b/dev/githubanalysis/lib/utils.dart deleted file mode 100644 index 7e2896425..000000000 --- a/dev/githubanalysis/lib/utils.dart +++ /dev/null @@ -1,49 +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:async'; -import 'dart:io'; - -import 'package:github/github.dart'; - -const Duration clockSkew = Duration(seconds: 5); -int minApiPoints = 250; // number of points to leave for debugging, etc - -class Abort implements Exception {} - -enum Mode { full, abbreviated, aborted } - -Mode mode = Mode.full; -final Completer aborter = Completer(); - -Future rateLimit(final GitHub github, final String status, final String next) async { - if (mode == Mode.aborted) { - throw Abort(); - } - stdout.write( - '$status${github.rateLimitRemaining != null ? ". Rate limits: ${github.rateLimitRemaining}/${github.rateLimitLimit} per hour" : ""}.\x1B[K\r', - ); - if (github.rateLimitRemaining != null && github.rateLimitRemaining! < minApiPoints) { - Duration delay = github.rateLimitReset!.difference(DateTime.now()); - if (delay > Duration.zero) { - delay += clockSkew; - print('\nWaiting until ${DateTime.now().add(delay).toLocal()} to continue with $next...'); - await Future.any(>[ - Future.delayed(delay), - aborter.future, - ]); - minApiPoints = 50; - } - } -} - -void verifyStringSanity(final String value, final Set disallowedSubstrings) { - for (final String substring in disallowedSubstrings) { - if (value.contains(substring)) { - throw FormatException('Found "$disallowedSubstrings" in "$value".'); - } - } -} - -DateTime maxAge(final Duration maxAge) => DateTime.now().subtract(maxAge); diff --git a/dev/githubanalysis/pubspec.yaml b/dev/githubanalysis/pubspec.yaml deleted file mode 100644 index 796a201a2..000000000 --- a/dev/githubanalysis/pubspec.yaml +++ /dev/null @@ -1,11 +0,0 @@ -name: githubanalysis - -environment: - sdk: '>=3.0.0-245.0.dev <4.0.0' - -dependencies: - github: ^9.9.0 - http: ^0.13.5 - -dev_dependencies: - lints: ^2.0.0 diff --git a/dev/provision_salt.sh b/dev/provision_salt.sh deleted file mode 100755 index 4ba433c23..000000000 --- a/dev/provision_salt.sh +++ /dev/null @@ -1,110 +0,0 @@ -#!/usr/bin/env bash -# 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. - -# This script provisions a salt minion on a Flutter test bed, it supports Linux and macOS. -set -e - -MINION_PLIST_PATH=/Library/LaunchDaemons/com.saltstack.salt.minion.plist - -# Installs salt minion. -# Pins the version to 2019.2.0 and Python 2 to be compatible with Fuchsia salt master. -function install_salt() { - if [[ "$(uname)" == 'Darwin' ]]; then - curl https://repo.saltproject.io/osx/salt-3002.9-py3-x86_64.pkg -o /tmp/salt.pkg - sudo installer -pkg /tmp/salt.pkg -target / - elif [[ "$(lsb_release -is)" == 'Debian' ]]; then - wget -O - https://repo.saltproject.io/py3/debian/10/amd64/3002/SALTSTACK-GPG-KEY.pub | sudo apt-key add - - echo 'deb http://repo.saltproject.io/py3/debian/10/amd64/3002 buster main' | sudo tee /etc/apt/sources.list.d/saltstack.list - # Also provision debian backports for m2crypto - echo 'deb http://deb.debian.org/debian buster-backports main' | sudo tee /etc/apt/sources.list.d/backports.list - sudo apt update - sudo apt install salt-minion - elif [[ "$(lsb_release -is)" == 'Ubuntu' ]]; then - sudo curl -fsSL -o /usr/share/keyrings/salt-archive-keyring.gpg https://repo.saltproject.io/py3/ubuntu/20.04/amd64/latest/salt-archive-keyring.gpg - echo 'deb [signed-by=/usr/share/keyrings/salt-archive-keyring.gpg arch=amd64] https://repo.saltproject.io/py3/ubuntu/18.04/amd64/3002 bionic main' | sudo tee /etc/apt/sources.list.d/salt.list - sudo apt update - sudo apt install salt-minion - fi -} - -function config_minion() { - sudo mkdir -p /etc/salt - echo "master: $1" | sudo tee /etc/salt/minion - # Uses hostname as the minion id. - echo "id: $(hostname -s)" | sudo tee -a /etc/salt/minion - - # Set fqdn for salt key autoaccept - echo "autosign_grains:" | sudo tee -a /etc/salt/minion - echo " - fqdn" | sudo tee -a /etc/salt/minion - - if [[ "$(uname)" == 'Darwin' ]]; then - sudo cp salt.minion.plist "$MINION_PLIST_PATH" - fi -} - -function set_deviceos_grains() { - if [ ! -z "$1" ]; then - sudo /opt/salt/bin/salt-call grains.set 'device_os' "$1" - fi -} - -function reboot_salt() { - if [[ "$(uname)" == 'Linux' ]]; then - sudo systemctl restart salt-minion - elif [[ "$(uname)" == 'Darwin' ]]; then - sudo launchctl unload "$MINION_PLIST_PATH" - sudo launchctl load -w "$MINION_PLIST_PATH" - fi -} - -function verify_provision() { - if sudo PATH="$PATH:/opt/salt/bin:/usr/bin" salt-minion --version; then - echo 'Succeed!' - else - echo 'Failed!' - fi -} - -function Usage() { - echo " -Usage: ./provision_salt.sh [SERVER] [DEVICE_OS] - - Arguments: - SERVER: required. Either 'prod' or 'dev'. - DEVICE_OS: optional. Either 'ios' or 'android'. - " -} - -function main() { - local master_hostname='' - # TODO(yusuf-goog): Update the hostname below when we get a dev flutter salt master. - case "$1" in - prod) master_hostname='salt-flutter.endpoints.fuchsia-infra.cloud.goog' ;; - dev) master_hostname='salt-flutter.endpoints.fuchsia-infra.cloud.goog' ;; - *) - Usage - exit 1 - ;; - esac - - local device_os='' - case "$2" in - ios) device_os='ios' ;; - android) device_os='android' ;; - "") device_os='' ;; - *) - Usage - exit 1 - ;; - esac - - install_salt - config_minion "$master_hostname" - reboot_salt - verify_provision - set_deviceos_grains "$device_os" -} - -main "$@" diff --git a/dev/prs_to_main.sh b/dev/prs_to_main.sh deleted file mode 100755 index 7580d1e1f..000000000 --- a/dev/prs_to_main.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env bash -# 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. - -# Run via ./prs_to_main.sh $GITHUB_USERNAME $GITHUB_TOKEN $REPOSITORY_NAME_WITH_OWNER -# -# To keep this script simple, you'll need to rerun this multiple times until it stops -# outputting PRs (indicating it processed all PRs) - -GITHUB_ACTOR=$1 -GITHUB_TOKEN=$2 -GITHUB_REPOSITORY=$3 -PREVIOUS_DEFAULT="master" -NEW_DEFAULT="main" -GITHUB_API_URL="https://api.github.com" - -# Check all existing PRs to see if we should change their base -PRS=$(curl --silent -u $GITHUB_ACTOR:$GITHUB_TOKEN "$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/pulls?base=$PREVIOUS_DEFAULT&state=open") - -for row in $(echo $PRS | jq -r 'map(select(.locked == false)) | .[].url'); do - echo "Attempting to update $row" - curl --silent --show-error -w "Status Code: %{http_code}\n" --request PATCH --data '{"base": "'$NEW_DEFAULT'"}' -u $GITHUB_ACTOR:$GITHUB_TOKEN "$row" -done diff --git a/dev/salt.minion.plist b/dev/salt.minion.plist deleted file mode 100644 index 983067c6b..000000000 --- a/dev/salt.minion.plist +++ /dev/null @@ -1,31 +0,0 @@ - - - - - Label - com.saltstack.salt.minion - RunAtLoad - - KeepAlive - - EnvironmentVariables - - PATH - /usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin:/opt/salt/bin - - ProgramArguments - - /opt/salt/bin/salt-minion - - SoftResourceLimits - - NumberOfFiles - 100000 - - HardResourceLimits - - NumberOfFiles - 100000 - - - diff --git a/dev/wiki-visualizer/.gitignore b/dev/wiki-visualizer/.gitignore deleted file mode 100644 index 3a8579040..000000000 --- a/dev/wiki-visualizer/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# https://dart.dev/guides/libraries/private-files -# Created by `dart pub` -.dart_tool/ diff --git a/dev/wiki-visualizer/README.md b/dev/wiki-visualizer/README.md deleted file mode 100644 index 812cb58bb..000000000 --- a/dev/wiki-visualizer/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# Wiki Visualizer - -When working on our wiki (https://github.com/flutter/flutter/wiki/) it -can help to see how the pages link together in real time. - -The following command allows one to check out the Flutter wiki: - -```bash -git clone git@github.com:flutter/flutter.wiki.git -``` - -Running the wiki_visualizer program from a checkout of the Flutter -wiki, and piping the output to the `sfdp` or `fdp` programs from the -Graphviz package, as follows, renders a PNG map of the wiki's internal -links: - -```bash -dart run --enable-asserts ../cocoon/dev/wiki-visualizer/bin/wiki_visualizer.dart *.md | sfdp -Tpng > map.png -``` - -(This assumes `cocoon` is checked out in a sibling directory of the -Flutter wiki.) - -For example, this image was rendered in this fashion: - -![](https://github.com/flutter/flutter/assets/551196/344a626a-9fda-4be1-9c76-2e884f920eec) diff --git a/dev/wiki-visualizer/bin/wiki_visualizer.dart b/dev/wiki-visualizer/bin/wiki_visualizer.dart deleted file mode 100644 index 76d897747..000000000 --- a/dev/wiki-visualizer/bin/wiki_visualizer.dart +++ /dev/null @@ -1,46 +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:io'; - -import 'package:wiki_visualizer/dot.dart'; -import 'package:wiki_visualizer/wiki_page.dart'; - -void main(final List arguments) { - if (arguments.isEmpty) { - print('Usage: dart run wiki_visualizer file1.md file2.md file3.md ...'); - print('Paths should be relative to the root of the wiki.'); - exit(1); - } - - final Wiki wiki = Wiki('https://github.com/flutter/flutter/wiki/'); - arguments.forEach(wiki.pageForFilename); - for (final WikiPage page in wiki.pages) { - page.parse(wiki); - } - - final WikiPage sidebar = wiki.pageForTitle('_Sidebar'); - final Set accessibleContent = sidebar.findAllAccessible(); - - print('strict digraph {'); - final StringBuffer attributes = StringBuffer(); - for (final WikiPage page in wiki.pages) { - if (page.sources.isEmpty) { - attributes.write(' [style=filled] [fillcolor="#FFCCCC"] [fontcolor=black]'); - } else if (!accessibleContent.contains(page)) { - attributes.write(' [color="#FF0000"]'); - } else if (page.sources.contains(sidebar)) { - attributes.write(' [style=filled] [fillcolor="#CCFFCC"] [fontcolor=black]'); - } - if (!page.parsed) { - attributes.write(' [fontcolor=silver]'); - } - print(' ${dotIdentifier(page.title)}$attributes'); - for (final WikiPage target in page.targets) { - print(' ${dotIdentifier(page.title)} -> ${dotIdentifier(target.title)}'); - } - attributes.clear(); - } - print('}'); -} diff --git a/dev/wiki-visualizer/lib/dot.dart b/dev/wiki-visualizer/lib/dot.dart deleted file mode 100644 index 42617b4a0..000000000 --- a/dev/wiki-visualizer/lib/dot.dart +++ /dev/null @@ -1,9 +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. - -String dotIdentifier(final String name) { - // TODO(ianh): properly escape strings for dot syntax - assert(!name.contains('"'), 'dotIdentifier does not yet implement proper escaping of quotes'); - return '"$name"'; -} diff --git a/dev/wiki-visualizer/lib/wiki_page.dart b/dev/wiki-visualizer/lib/wiki_page.dart deleted file mode 100644 index dc1fb3501..000000000 --- a/dev/wiki-visualizer/lib/wiki_page.dart +++ /dev/null @@ -1,558 +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:io'; - -class Wiki { - Wiki(this.root); - - final String root; - - final Map _pages = {}; - - Set get pages => _pages.values.toSet(); - - WikiPage pageForFilename(final String filename) { - return _pages.putIfAbsent(Uri.decodeFull(filename).toLowerCase(), () => WikiPage(filename)); - } - - WikiPage? pageForUrl(final String url) { - if (url.startsWith(root)) { - int end = url.indexOf('#'); - if (end < 0) { - end = url.length; - } - return pageForFilename('${url.substring(root.length, end)}.md'); - } - return null; - } - - WikiPage pageForTitle(final String title) { - final String filename = '${title.replaceAll(' ', '-')}.md'; - return pageForFilename(filename); - } -} - -class WikiPage { - WikiPage(this.filename); - - final String filename; - - final Set sources = {}; // pages pointing to us - final Set targets = {}; // pages we point to - - bool parsed = false; - - String get title { - assert(filename.endsWith('.md'), 'not sure how to handle non-markdown files'); - return Uri.decodeFull(filename.substring(0, filename.length - 3)).replaceAll('-', ' '); - } - - void parse(final Wiki wiki) { - parseFromString(wiki, File(filename).readAsStringSync()); - } - - void parseFromString(final Wiki wiki, final String body) { - assert(!parsed, 'cannot parse a wiki page twice'); - _parseTokens(wiki, _tokenize(body.runes)); - } - - void _parseTokens(final Wiki wiki, final List<_Token> tokens) { - final Set footnoteLinks = {}; - final Map footnoteDefinitions = {}; - for (final _Token token in tokens) { - switch (token) { - case _TextToken(): - break; - case _InternalLinkToken(options: final List options): - if (options.any( - (final String option) => - option.startsWith('width=') || option.startsWith('height=') || option.startsWith('alt='), - )) { - // it's an image; ignore it. - } else { - String title = options.last; - if (title.contains('#')) { - title = title.substring(0, title.indexOf('#')); - } - if (title.isNotEmpty) { - final WikiPage page = wiki.pageForTitle(title); - if (page != this) { - targets.add(page); - page.sources.add(this); - } - } - } - case _HyperlinkToken(url: final String url): - final WikiPage? page = wiki.pageForUrl(url); - if (page != null && page != this) { - targets.add(page); - page.sources.add(this); - } - case _FootnoteLinkToken(footnote: final String footnote): - footnoteLinks.add(_cleanFootnote(footnote)); - case _FootnoteDefinitionToken(label: final String label, url: final String url): - if (footnoteDefinitions.containsKey(label)) { - stderr.writeln('$filename: duplicate footnote "$label"'); - } - footnoteDefinitions[_cleanFootnote(label)] = url; - case _ErrorToken(message: final String message, line: final int line, column: final int column): - stderr.writeln('$filename:$line:$column: $message'); - } - } - for (final String footnote in footnoteLinks) { - if (footnoteDefinitions.containsKey(footnote)) { - final WikiPage? page = wiki.pageForUrl(footnoteDefinitions[footnote]!); - if (page != null && page != this) { - targets.add(page); - page.sources.add(this); - } - } - } - parsed = true; - } - - final RegExp whitespace = RegExp(r'[ \n]+'); - - String _cleanFootnote(final String label) { - return label.replaceAll(whitespace, ' '); - } - - Set findAllAccessible() { - final Set result = {}; - final Set stack = {this}; - while (stack.isNotEmpty) { - final WikiPage current = stack.first; - stack.remove(current); - if (result.contains(current)) { - continue; - } - result.add(current); - stack.addAll(current.targets); - } - return result; - } -} - -final class _Token {} - -final class _TextToken extends _Token { - _TextToken(this.text); - final String text; -} - -final class _InternalLinkToken extends _Token { - _InternalLinkToken(this.label, this.options); - final String label; - final List options; -} - -final class _HyperlinkToken extends _Token { - _HyperlinkToken(this.label, this.url); - final String label; - final String url; -} - -final class _FootnoteLinkToken extends _Token { - _FootnoteLinkToken(this.label, this.footnote); - final String label; - final String footnote; -} - -final class _FootnoteDefinitionToken extends _Token { - _FootnoteDefinitionToken(this.label, this.url); - final String label; - final String url; -} - -final class _ErrorToken extends _Token { - _ErrorToken(this.message, this.line, this.column); - final String message; - final int line; - final int column; -} - -enum _Mode { - normal, - linkStart, - internalLinkText, - internalLinkTextEnd, - internalLinkPage, - internalLinkPageEnd, - hyperlinkText, - linkHyperlinkSeparators, - hyperlinkUrl, - footnoteLabel, - footnoteUrl, - backtick1, - backtick2, - codeLine, - codeBlock, - codeBlockBacktick1, - codeBlockBacktick2, - eof, -} - -List<_Token> _tokenize(final Iterable input) { - final List<_Token> results = <_Token>[]; - _Mode mode = _Mode.normal; - final List buffer = []; - final List spaceBuffer = []; - final List linkBuffer = []; - int line = 1; - int column = 1; - void flushBuffer() { - if (buffer.isNotEmpty) { - results.add(_TextToken(String.fromCharCodes(buffer))); - buffer.clear(); - } - } - - void error(final String message) { - results.add(_ErrorToken(message, line, column)); - } - - void process(final int character) { - if (character == 0x0A) { - line += 1; - column = 0; - } - column += 1; - retokenize: - while (true) { - switch (mode) { - case _Mode.normal: - switch (character) { - case -1: // EOF - mode = _Mode.eof; - return; - case 0x5B: // U+005B LEFT SQUARE BRACKET character ([) - flushBuffer(); - mode = _Mode.linkStart; - return; - case 0x60: // U+0060 GRAVE ACCENT character (`) - mode = _Mode.backtick1; - return; - default: - buffer.add(character); - return; - } - - case _Mode.linkStart: - switch (character) { - case -1: // EOF - error('unexpected EOF in link'); - buffer.insert(0, 0x5B); - mode = _Mode.eof; - return; - case 0x5B: // U+005B LEFT SQUARE BRACKET character ([) - mode = _Mode.internalLinkText; - return; - default: - mode = _Mode.hyperlinkText; - continue retokenize; - } - - case _Mode.internalLinkText: - switch (character) { - case -1: // EOF - error('unexpected EOF in internal link'); - buffer.insert(0, 0x5B); - buffer.insert(0, 0x5B); - mode = _Mode.eof; - return; - case 0x5D: // U+005D RIGHT SQUARE BRACKET character (]) - mode = _Mode.internalLinkTextEnd; - return; - case 0x7C: // U+007C VERTICAL LINE character (|) - mode = _Mode.internalLinkPage; - assert(linkBuffer.isEmpty, 'internal violation'); - return; - default: - buffer.add(character); - return; - } - - case _Mode.internalLinkTextEnd: - switch (character) { - case -1: // EOF - error('unexpected EOF after internal link'); - buffer.insert(0, 0x5B); - buffer.insert(0, 0x5B); - buffer.add(0x5D); - mode = _Mode.eof; - return; - case 0x5D: // U+005D RIGHT SQUARE BRACKET character (]) - mode = _Mode.normal; - results.add(_InternalLinkToken(String.fromCharCodes(buffer), [String.fromCharCodes(buffer)])); - buffer.clear(); - return; - default: - buffer.insert(0, 0x5B); - buffer.insert(0, 0x5B); - buffer.add(0x7C); - mode = _Mode.hyperlinkText; - continue retokenize; - } - - case _Mode.internalLinkPage: - switch (character) { - case -1: // EOF - error('unexpected EOF in internal link page name'); - buffer.insert(0, 0x5B); - buffer.insert(0, 0x5B); - buffer.add(0x7C); - buffer.addAll(linkBuffer); - linkBuffer.clear(); - mode = _Mode.eof; - return; - case 0x5D: // U+005D RIGHT SQUARE BRACKET character (]) - mode = _Mode.internalLinkPageEnd; - return; - default: - linkBuffer.add(character); - return; - } - - case _Mode.internalLinkPageEnd: - switch (character) { - case -1: // EOF - error('unexpected EOF after internal link'); - buffer.insert(0, 0x5B); - buffer.insert(0, 0x5B); - buffer.add(0x7C); - buffer.addAll(linkBuffer); - buffer.add(0x5D); - mode = _Mode.eof; - return; - case 0x5D: // U+005D RIGHT SQUARE BRACKET character (]) - results.add( - _InternalLinkToken( - String.fromCharCodes(buffer), - String.fromCharCodes(linkBuffer).split('|').toList(), - ), - ); - buffer.clear(); - linkBuffer.clear(); - mode = _Mode.normal; - return; - default: - linkBuffer.add(0x5D); - linkBuffer.add(character); - return; - } - - case _Mode.hyperlinkText: - switch (character) { - case -1: // EOF - buffer.insert(0, 0x5B); - mode = _Mode.eof; - return; - case 0x5D: // U+005D RIGHT SQUARE BRACKET character (]) - mode = _Mode.linkHyperlinkSeparators; - assert(spaceBuffer.isEmpty, 'internal violation'); - return; - default: - buffer.add(character); - return; - } - - case _Mode.linkHyperlinkSeparators: - switch (character) { - case -1: // EOF - buffer.insert(0, 0x5B); - buffer.add(0x5D); - buffer.addAll(spaceBuffer); - mode = _Mode.eof; - return; - case 0x0A: - case 0x20: - spaceBuffer.add(character); - return; - case 0x28: // U+0028 LEFT PARENTHESIS character (() - mode = _Mode.hyperlinkUrl; - assert(linkBuffer.isEmpty, 'internal violation'); - return; - case 0x3A: // U+003A COLON character (:) - mode = _Mode.footnoteUrl; - assert(linkBuffer.isEmpty, 'internal violation'); - return; - case 0x5B: // U+005B LEFT SQUARE BRACKET character ([) - mode = _Mode.footnoteLabel; - assert(linkBuffer.isEmpty, 'internal violation'); - return; - default: - buffer.insert(0, 0x5B); - buffer.add(0x5D); - buffer.addAll(spaceBuffer); - spaceBuffer.clear(); - mode = _Mode.normal; - continue retokenize; - } - - case _Mode.hyperlinkUrl: - switch (character) { - case -1: // EOF - buffer.insert(0, 0x5B); - buffer.add(0x5D); - buffer.addAll(spaceBuffer); - buffer.add(0x28); - buffer.addAll(linkBuffer); - mode = _Mode.eof; - return; - case 0x29: // U+0029 RIGHT PARENTHESIS character ()) - results.add(_HyperlinkToken(String.fromCharCodes(buffer), String.fromCharCodes(linkBuffer))); - buffer.clear(); - spaceBuffer.clear(); - linkBuffer.clear(); - mode = _Mode.normal; - return; - default: - linkBuffer.add(character); - return; - } - - case _Mode.footnoteUrl: - switch (character) { - case -1: // EOF - case 0x0A: // newline - results - .add(_FootnoteDefinitionToken(String.fromCharCodes(buffer), String.fromCharCodes(linkBuffer).trim())); - buffer.clear(); - spaceBuffer.clear(); - linkBuffer.clear(); - mode = _Mode.normal; - continue retokenize; - default: - linkBuffer.add(character); - return; - } - - case _Mode.footnoteLabel: - switch (character) { - case -1: // EOF - buffer.insert(0, 0x5B); - buffer.add(0x5D); - buffer.addAll(spaceBuffer); - buffer.add(0x5B); - buffer.addAll(linkBuffer); - mode = _Mode.eof; - return; - case 0x5D: // U+005D RIGHT SQUARE BRACKET character (]) - if (linkBuffer.isEmpty) { - results.add(_FootnoteLinkToken(String.fromCharCodes(buffer), String.fromCharCodes(buffer))); - } else { - results.add(_FootnoteLinkToken(String.fromCharCodes(buffer), String.fromCharCodes(linkBuffer))); - } - buffer.clear(); - spaceBuffer.clear(); - linkBuffer.clear(); - mode = _Mode.normal; - return; - default: - linkBuffer.add(character); - return; - } - - case _Mode.backtick1: - switch (character) { - case -1: // EOF - mode = _Mode.eof; - return; - case 0x60: // U+0060 GRAVE ACCENT character (`) - buffer.add(character); - mode = _Mode.backtick2; - return; - default: - buffer.add(character); - mode = _Mode.codeLine; - return; - } - - case _Mode.backtick2: - switch (character) { - case -1: // EOF - buffer.add(character); - mode = _Mode.eof; - return; - case 0x60: // U+0060 GRAVE ACCENT character (`) - buffer.add(character); - mode = _Mode.codeBlock; - return; - default: - mode = _Mode.normal; - continue retokenize; - } - - case _Mode.codeBlock: - switch (character) { - case -1: // EOF - mode = _Mode.eof; - return; - case 0x60: // U+0060 GRAVE ACCENT character (`) - buffer.add(character); - mode = _Mode.codeBlockBacktick1; - return; - default: - buffer.add(character); - return; - } - - case _Mode.codeLine: - switch (character) { - case -1: // EOF - mode = _Mode.eof; - return; - case 0x60: // U+0060 GRAVE ACCENT character (`) - buffer.add(character); - mode = _Mode.normal; - return; - default: - buffer.add(character); - return; - } - - case _Mode.codeBlockBacktick1: - switch (character) { - case -1: // EOF - mode = _Mode.eof; - return; - case 0x60: // U+0060 GRAVE ACCENT character (`) - buffer.add(character); - mode = _Mode.codeBlockBacktick2; - return; - default: - buffer.add(character); - mode = _Mode.codeBlock; - return; - } - - case _Mode.codeBlockBacktick2: - switch (character) { - case -1: // EOF - mode = _Mode.eof; - return; - case 0x60: // U+0060 GRAVE ACCENT character (`) - buffer.add(character); - mode = _Mode.normal; - return; - default: - buffer.add(character); - mode = _Mode.codeBlock; - return; - } - - case _Mode.eof: - assert(false, 'internal violation'); - } - } - } - - input.forEach(process); - assert(mode != _Mode.eof, 'internal violation'); - process(-1); - assert(mode == _Mode.eof, 'internal violation'); - flushBuffer(); - return results; -} diff --git a/dev/wiki-visualizer/pubspec.yaml b/dev/wiki-visualizer/pubspec.yaml deleted file mode 100644 index b054350e0..000000000 --- a/dev/wiki-visualizer/pubspec.yaml +++ /dev/null @@ -1,10 +0,0 @@ -name: wiki_visualizer -description: A sample command-line application. -version: 1.0.0 - -environment: - sdk: ^3.2.0-38.0.dev - -dev_dependencies: - lints: ^2.0.0 - test: ^1.21.0 diff --git a/dev/wiki-visualizer/test/general_test.dart b/dev/wiki-visualizer/test/general_test.dart deleted file mode 100644 index a55b78b39..000000000 --- a/dev/wiki-visualizer/test/general_test.dart +++ /dev/null @@ -1,23 +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:test/test.dart'; - -import 'package:wiki_visualizer/wiki_page.dart'; - -void main() { - test('smoke test', () { - final Wiki wiki = Wiki('https://example.com/'); - wiki.pageForFilename('a.md').parseFromString(wiki, '[[b]]'); - wiki.pageForFilename('b.md').parseFromString(wiki, '[[x|c]] [y](https://example.com/d)'); - expect(wiki.pageForTitle('A').targets, {wiki.pageForTitle('B')}); - expect(wiki.pageForTitle('A').parsed, isTrue); - expect(wiki.pageForTitle('B').targets, {wiki.pageForTitle('C'), wiki.pageForTitle('D')}); - expect(wiki.pageForTitle('B').parsed, isTrue); - expect(wiki.pageForTitle('C').targets, isEmpty); - expect(wiki.pageForTitle('C').parsed, isFalse); - expect(wiki.pageForTitle('D').targets, isEmpty); - expect(wiki.pageForTitle('D').parsed, isFalse); - }); -} diff --git a/dist/index.js b/dist/index.js new file mode 100644 index 000000000..99618fcf8 --- /dev/null +++ b/dist/index.js @@ -0,0 +1,37550 @@ +/******/ (() => { // webpackBootstrap +/******/ var __webpack_modules__ = ({ + +/***/ 7351: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.issue = exports.issueCommand = void 0; +const os = __importStar(__nccwpck_require__(2037)); +const utils_1 = __nccwpck_require__(5278); +/** + * Commands + * + * Command Format: + * ::name key=value,key=value::message + * + * Examples: + * ::warning::This is the message + * ::set-env name=MY_VAR::some value + */ +function issueCommand(command, properties, message) { + const cmd = new Command(command, properties, message); + process.stdout.write(cmd.toString() + os.EOL); +} +exports.issueCommand = issueCommand; +function issue(name, message = '') { + issueCommand(name, {}, message); +} +exports.issue = issue; +const CMD_STRING = '::'; +class Command { + constructor(command, properties, message) { + if (!command) { + command = 'missing.command'; + } + this.command = command; + this.properties = properties; + this.message = message; + } + toString() { + let cmdStr = CMD_STRING + this.command; + if (this.properties && Object.keys(this.properties).length > 0) { + cmdStr += ' '; + let first = true; + for (const key in this.properties) { + if (this.properties.hasOwnProperty(key)) { + const val = this.properties[key]; + if (val) { + if (first) { + first = false; + } + else { + cmdStr += ','; + } + cmdStr += `${key}=${escapeProperty(val)}`; + } + } + } + } + cmdStr += `${CMD_STRING}${escapeData(this.message)}`; + return cmdStr; + } +} +function escapeData(s) { + return utils_1.toCommandValue(s) + .replace(/%/g, '%25') + .replace(/\r/g, '%0D') + .replace(/\n/g, '%0A'); +} +function escapeProperty(s) { + return utils_1.toCommandValue(s) + .replace(/%/g, '%25') + .replace(/\r/g, '%0D') + .replace(/\n/g, '%0A') + .replace(/:/g, '%3A') + .replace(/,/g, '%2C'); +} +//# sourceMappingURL=command.js.map + +/***/ }), + +/***/ 2186: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.getIDToken = exports.getState = exports.saveState = exports.group = exports.endGroup = exports.startGroup = exports.info = exports.notice = exports.warning = exports.error = exports.debug = exports.isDebug = exports.setFailed = exports.setCommandEcho = exports.setOutput = exports.getBooleanInput = exports.getMultilineInput = exports.getInput = exports.addPath = exports.setSecret = exports.exportVariable = exports.ExitCode = void 0; +const command_1 = __nccwpck_require__(7351); +const file_command_1 = __nccwpck_require__(717); +const utils_1 = __nccwpck_require__(5278); +const os = __importStar(__nccwpck_require__(2037)); +const path = __importStar(__nccwpck_require__(1017)); +const oidc_utils_1 = __nccwpck_require__(8041); +/** + * The code to exit an action + */ +var ExitCode; +(function (ExitCode) { + /** + * A code indicating that the action was successful + */ + ExitCode[ExitCode["Success"] = 0] = "Success"; + /** + * A code indicating that the action was a failure + */ + ExitCode[ExitCode["Failure"] = 1] = "Failure"; +})(ExitCode = exports.ExitCode || (exports.ExitCode = {})); +//----------------------------------------------------------------------- +// Variables +//----------------------------------------------------------------------- +/** + * Sets env variable for this action and future actions in the job + * @param name the name of the variable to set + * @param val the value of the variable. Non-string values will be converted to a string via JSON.stringify + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function exportVariable(name, val) { + const convertedVal = utils_1.toCommandValue(val); + process.env[name] = convertedVal; + const filePath = process.env['GITHUB_ENV'] || ''; + if (filePath) { + return file_command_1.issueFileCommand('ENV', file_command_1.prepareKeyValueMessage(name, val)); + } + command_1.issueCommand('set-env', { name }, convertedVal); +} +exports.exportVariable = exportVariable; +/** + * Registers a secret which will get masked from logs + * @param secret value of the secret + */ +function setSecret(secret) { + command_1.issueCommand('add-mask', {}, secret); +} +exports.setSecret = setSecret; +/** + * Prepends inputPath to the PATH (for this action and future actions) + * @param inputPath + */ +function addPath(inputPath) { + const filePath = process.env['GITHUB_PATH'] || ''; + if (filePath) { + file_command_1.issueFileCommand('PATH', inputPath); + } + else { + command_1.issueCommand('add-path', {}, inputPath); + } + process.env['PATH'] = `${inputPath}${path.delimiter}${process.env['PATH']}`; +} +exports.addPath = addPath; +/** + * Gets the value of an input. + * Unless trimWhitespace is set to false in InputOptions, the value is also trimmed. + * Returns an empty string if the value is not defined. + * + * @param name name of the input to get + * @param options optional. See InputOptions. + * @returns string + */ +function getInput(name, options) { + const val = process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || ''; + if (options && options.required && !val) { + throw new Error(`Input required and not supplied: ${name}`); + } + if (options && options.trimWhitespace === false) { + return val; + } + return val.trim(); +} +exports.getInput = getInput; +/** + * Gets the values of an multiline input. Each value is also trimmed. + * + * @param name name of the input to get + * @param options optional. See InputOptions. + * @returns string[] + * + */ +function getMultilineInput(name, options) { + const inputs = getInput(name, options) + .split('\n') + .filter(x => x !== ''); + if (options && options.trimWhitespace === false) { + return inputs; + } + return inputs.map(input => input.trim()); +} +exports.getMultilineInput = getMultilineInput; +/** + * Gets the input value of the boolean type in the YAML 1.2 "core schema" specification. + * Support boolean input list: `true | True | TRUE | false | False | FALSE` . + * The return value is also in boolean type. + * ref: https://yaml.org/spec/1.2/spec.html#id2804923 + * + * @param name name of the input to get + * @param options optional. See InputOptions. + * @returns boolean + */ +function getBooleanInput(name, options) { + const trueValue = ['true', 'True', 'TRUE']; + const falseValue = ['false', 'False', 'FALSE']; + const val = getInput(name, options); + if (trueValue.includes(val)) + return true; + if (falseValue.includes(val)) + return false; + throw new TypeError(`Input does not meet YAML 1.2 "Core Schema" specification: ${name}\n` + + `Support boolean input list: \`true | True | TRUE | false | False | FALSE\``); +} +exports.getBooleanInput = getBooleanInput; +/** + * Sets the value of an output. + * + * @param name name of the output to set + * @param value value to store. Non-string values will be converted to a string via JSON.stringify + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function setOutput(name, value) { + const filePath = process.env['GITHUB_OUTPUT'] || ''; + if (filePath) { + return file_command_1.issueFileCommand('OUTPUT', file_command_1.prepareKeyValueMessage(name, value)); + } + process.stdout.write(os.EOL); + command_1.issueCommand('set-output', { name }, utils_1.toCommandValue(value)); +} +exports.setOutput = setOutput; +/** + * Enables or disables the echoing of commands into stdout for the rest of the step. + * Echoing is disabled by default if ACTIONS_STEP_DEBUG is not set. + * + */ +function setCommandEcho(enabled) { + command_1.issue('echo', enabled ? 'on' : 'off'); +} +exports.setCommandEcho = setCommandEcho; +//----------------------------------------------------------------------- +// Results +//----------------------------------------------------------------------- +/** + * Sets the action status to failed. + * When the action exits it will be with an exit code of 1 + * @param message add error issue message + */ +function setFailed(message) { + process.exitCode = ExitCode.Failure; + error(message); +} +exports.setFailed = setFailed; +//----------------------------------------------------------------------- +// Logging Commands +//----------------------------------------------------------------------- +/** + * Gets whether Actions Step Debug is on or not + */ +function isDebug() { + return process.env['RUNNER_DEBUG'] === '1'; +} +exports.isDebug = isDebug; +/** + * Writes debug message to user log + * @param message debug message + */ +function debug(message) { + command_1.issueCommand('debug', {}, message); +} +exports.debug = debug; +/** + * Adds an error issue + * @param message error issue message. Errors will be converted to string via toString() + * @param properties optional properties to add to the annotation. + */ +function error(message, properties = {}) { + command_1.issueCommand('error', utils_1.toCommandProperties(properties), message instanceof Error ? message.toString() : message); +} +exports.error = error; +/** + * Adds a warning issue + * @param message warning issue message. Errors will be converted to string via toString() + * @param properties optional properties to add to the annotation. + */ +function warning(message, properties = {}) { + command_1.issueCommand('warning', utils_1.toCommandProperties(properties), message instanceof Error ? message.toString() : message); +} +exports.warning = warning; +/** + * Adds a notice issue + * @param message notice issue message. Errors will be converted to string via toString() + * @param properties optional properties to add to the annotation. + */ +function notice(message, properties = {}) { + command_1.issueCommand('notice', utils_1.toCommandProperties(properties), message instanceof Error ? message.toString() : message); +} +exports.notice = notice; +/** + * Writes info to log with console.log. + * @param message info message + */ +function info(message) { + process.stdout.write(message + os.EOL); +} +exports.info = info; +/** + * Begin an output group. + * + * Output until the next `groupEnd` will be foldable in this group + * + * @param name The name of the output group + */ +function startGroup(name) { + command_1.issue('group', name); +} +exports.startGroup = startGroup; +/** + * End an output group. + */ +function endGroup() { + command_1.issue('endgroup'); +} +exports.endGroup = endGroup; +/** + * Wrap an asynchronous function call in a group. + * + * Returns the same type as the function itself. + * + * @param name The name of the group + * @param fn The function to wrap in the group + */ +function group(name, fn) { + return __awaiter(this, void 0, void 0, function* () { + startGroup(name); + let result; + try { + result = yield fn(); + } + finally { + endGroup(); + } + return result; + }); +} +exports.group = group; +//----------------------------------------------------------------------- +// Wrapper action state +//----------------------------------------------------------------------- +/** + * Saves state for current action, the state can only be retrieved by this action's post job execution. + * + * @param name name of the state to store + * @param value value to store. Non-string values will be converted to a string via JSON.stringify + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function saveState(name, value) { + const filePath = process.env['GITHUB_STATE'] || ''; + if (filePath) { + return file_command_1.issueFileCommand('STATE', file_command_1.prepareKeyValueMessage(name, value)); + } + command_1.issueCommand('save-state', { name }, utils_1.toCommandValue(value)); +} +exports.saveState = saveState; +/** + * Gets the value of an state set by this action's main execution. + * + * @param name name of the state to get + * @returns string + */ +function getState(name) { + return process.env[`STATE_${name}`] || ''; +} +exports.getState = getState; +function getIDToken(aud) { + return __awaiter(this, void 0, void 0, function* () { + return yield oidc_utils_1.OidcClient.getIDToken(aud); + }); +} +exports.getIDToken = getIDToken; +/** + * Summary exports + */ +var summary_1 = __nccwpck_require__(1327); +Object.defineProperty(exports, "summary", ({ enumerable: true, get: function () { return summary_1.summary; } })); +/** + * @deprecated use core.summary + */ +var summary_2 = __nccwpck_require__(1327); +Object.defineProperty(exports, "markdownSummary", ({ enumerable: true, get: function () { return summary_2.markdownSummary; } })); +/** + * Path exports + */ +var path_utils_1 = __nccwpck_require__(2981); +Object.defineProperty(exports, "toPosixPath", ({ enumerable: true, get: function () { return path_utils_1.toPosixPath; } })); +Object.defineProperty(exports, "toWin32Path", ({ enumerable: true, get: function () { return path_utils_1.toWin32Path; } })); +Object.defineProperty(exports, "toPlatformPath", ({ enumerable: true, get: function () { return path_utils_1.toPlatformPath; } })); +//# sourceMappingURL=core.js.map + +/***/ }), + +/***/ 717: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +// For internal use, subject to change. +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.prepareKeyValueMessage = exports.issueFileCommand = void 0; +// We use any as a valid input type +/* eslint-disable @typescript-eslint/no-explicit-any */ +const fs = __importStar(__nccwpck_require__(7147)); +const os = __importStar(__nccwpck_require__(2037)); +const uuid_1 = __nccwpck_require__(5840); +const utils_1 = __nccwpck_require__(5278); +function issueFileCommand(command, message) { + const filePath = process.env[`GITHUB_${command}`]; + if (!filePath) { + throw new Error(`Unable to find environment variable for file command ${command}`); + } + if (!fs.existsSync(filePath)) { + throw new Error(`Missing file at path: ${filePath}`); + } + fs.appendFileSync(filePath, `${utils_1.toCommandValue(message)}${os.EOL}`, { + encoding: 'utf8' + }); +} +exports.issueFileCommand = issueFileCommand; +function prepareKeyValueMessage(key, value) { + const delimiter = `ghadelimiter_${uuid_1.v4()}`; + const convertedValue = utils_1.toCommandValue(value); + // These should realistically never happen, but just in case someone finds a + // way to exploit uuid generation let's not allow keys or values that contain + // the delimiter. + if (key.includes(delimiter)) { + throw new Error(`Unexpected input: name should not contain the delimiter "${delimiter}"`); + } + if (convertedValue.includes(delimiter)) { + throw new Error(`Unexpected input: value should not contain the delimiter "${delimiter}"`); + } + return `${key}<<${delimiter}${os.EOL}${convertedValue}${os.EOL}${delimiter}`; +} +exports.prepareKeyValueMessage = prepareKeyValueMessage; +//# sourceMappingURL=file-command.js.map + +/***/ }), + +/***/ 8041: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.OidcClient = void 0; +const http_client_1 = __nccwpck_require__(6255); +const auth_1 = __nccwpck_require__(5526); +const core_1 = __nccwpck_require__(2186); +class OidcClient { + static createHttpClient(allowRetry = true, maxRetry = 10) { + const requestOptions = { + allowRetries: allowRetry, + maxRetries: maxRetry + }; + return new http_client_1.HttpClient('actions/oidc-client', [new auth_1.BearerCredentialHandler(OidcClient.getRequestToken())], requestOptions); + } + static getRequestToken() { + const token = process.env['ACTIONS_ID_TOKEN_REQUEST_TOKEN']; + if (!token) { + throw new Error('Unable to get ACTIONS_ID_TOKEN_REQUEST_TOKEN env variable'); + } + return token; + } + static getIDTokenUrl() { + const runtimeUrl = process.env['ACTIONS_ID_TOKEN_REQUEST_URL']; + if (!runtimeUrl) { + throw new Error('Unable to get ACTIONS_ID_TOKEN_REQUEST_URL env variable'); + } + return runtimeUrl; + } + static getCall(id_token_url) { + var _a; + return __awaiter(this, void 0, void 0, function* () { + const httpclient = OidcClient.createHttpClient(); + const res = yield httpclient + .getJson(id_token_url) + .catch(error => { + throw new Error(`Failed to get ID Token. \n + Error Code : ${error.statusCode}\n + Error Message: ${error.message}`); + }); + const id_token = (_a = res.result) === null || _a === void 0 ? void 0 : _a.value; + if (!id_token) { + throw new Error('Response json body do not have ID Token field'); + } + return id_token; + }); + } + static getIDToken(audience) { + return __awaiter(this, void 0, void 0, function* () { + try { + // New ID Token is requested from action service + let id_token_url = OidcClient.getIDTokenUrl(); + if (audience) { + const encodedAudience = encodeURIComponent(audience); + id_token_url = `${id_token_url}&audience=${encodedAudience}`; + } + core_1.debug(`ID token url is ${id_token_url}`); + const id_token = yield OidcClient.getCall(id_token_url); + core_1.setSecret(id_token); + return id_token; + } + catch (error) { + throw new Error(`Error message: ${error.message}`); + } + }); + } +} +exports.OidcClient = OidcClient; +//# sourceMappingURL=oidc-utils.js.map + +/***/ }), + +/***/ 2981: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.toPlatformPath = exports.toWin32Path = exports.toPosixPath = void 0; +const path = __importStar(__nccwpck_require__(1017)); +/** + * toPosixPath converts the given path to the posix form. On Windows, \\ will be + * replaced with /. + * + * @param pth. Path to transform. + * @return string Posix path. + */ +function toPosixPath(pth) { + return pth.replace(/[\\]/g, '/'); +} +exports.toPosixPath = toPosixPath; +/** + * toWin32Path converts the given path to the win32 form. On Linux, / will be + * replaced with \\. + * + * @param pth. Path to transform. + * @return string Win32 path. + */ +function toWin32Path(pth) { + return pth.replace(/[/]/g, '\\'); +} +exports.toWin32Path = toWin32Path; +/** + * toPlatformPath converts the given path to a platform-specific path. It does + * this by replacing instances of / and \ with the platform-specific path + * separator. + * + * @param pth The path to platformize. + * @return string The platform-specific path. + */ +function toPlatformPath(pth) { + return pth.replace(/[/\\]/g, path.sep); +} +exports.toPlatformPath = toPlatformPath; +//# sourceMappingURL=path-utils.js.map + +/***/ }), + +/***/ 1327: +/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) { + +"use strict"; + +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +Object.defineProperty(exports, "__esModule", ({ value: true })); +exports.summary = exports.markdownSummary = exports.SUMMARY_DOCS_URL = exports.SUMMARY_ENV_VAR = void 0; +const os_1 = __nccwpck_require__(2037); +const fs_1 = __nccwpck_require__(7147); +const { access, appendFile, writeFile } = fs_1.promises; +exports.SUMMARY_ENV_VAR = 'GITHUB_STEP_SUMMARY'; +exports.SUMMARY_DOCS_URL = 'https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-job-summary'; +class Summary { + constructor() { + this._buffer = ''; + } + /** + * Finds the summary file path from the environment, rejects if env var is not found or file does not exist + * Also checks r/w permissions. + * + * @returns step summary file path + */ + filePath() { + return __awaiter(this, void 0, void 0, function* () { + if (this._filePath) { + return this._filePath; + } + const pathFromEnv = process.env[exports.SUMMARY_ENV_VAR]; + if (!pathFromEnv) { + throw new Error(`Unable to find environment variable for $${exports.SUMMARY_ENV_VAR}. Check if your runtime environment supports job summaries.`); + } + try { + yield access(pathFromEnv, fs_1.constants.R_OK | fs_1.constants.W_OK); + } + catch (_a) { + throw new Error(`Unable to access summary file: '${pathFromEnv}'. Check if the file has correct read/write permissions.`); + } + this._filePath = pathFromEnv; + return this._filePath; + }); + } + /** + * Wraps content in an HTML tag, adding any HTML attributes + * + * @param {string} tag HTML tag to wrap + * @param {string | null} content content within the tag + * @param {[attribute: string]: string} attrs key-value list of HTML attributes to add + * + * @returns {string} content wrapped in HTML element + */ + wrap(tag, content, attrs = {}) { + const htmlAttrs = Object.entries(attrs) + .map(([key, value]) => ` ${key}="${value}"`) + .join(''); + if (!content) { + return `<${tag}${htmlAttrs}>`; + } + return `<${tag}${htmlAttrs}>${content}`; + } + /** + * Writes text in the buffer to the summary buffer file and empties buffer. Will append by default. + * + * @param {SummaryWriteOptions} [options] (optional) options for write operation + * + * @returns {Promise