Explorar o código

Update binary size tool to connect to the Metrics Service in postsubmits. (#8550)

Update binary size tool to connect to the Metrics Service.
Update condition and set postsubmit flag
Gran %!s(int64=4) %!d(string=hai) anos
pai
achega
77dae88bb9

+ 21 - 1
.github/workflows/health_metrics.yml

@@ -11,6 +11,8 @@ env:
   METRICS_SERVICE_SECRET: ${{ secrets.GHASecretsGPGPassphrase1 }}
 
 jobs:
+  # Check all the modified SDKs, the flags will be true if changed files match patterns in the file
+  # scripts/health_metrics/code_coverage_file_list.json
   check:
     if: github.repository == 'Firebase/firebase-ios-sdk' && (github.event.action == 'opened' || github.event.action == 'synchronize')
     name: Check changed files
@@ -44,13 +46,26 @@ jobs:
 
   binary_size_metrics:
     needs: check
-    if: always() && github.repository == 'Firebase/firebase-ios-sdk' && (github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.pull_request.merged)
+    # Prevent the job from being triggered in fork.
+    if: always() && github.repository == 'Firebase/firebase-ios-sdk' && github.event.pull_request.merged
     runs-on: macos-11
     strategy:
       matrix:
         target: [iOS]
     steps:
     - uses: actions/checkout@v2
+    - name: Access to Metrics Service
+      run: |
+        # Install gcloud sdk
+        curl https://sdk.cloud.google.com > install.sh
+        bash install.sh --disable-prompts
+        echo "${HOME}/google-cloud-sdk/bin/" >> $GITHUB_PATH
+        export PATH="${HOME}/google-cloud-sdk/bin/:${PATH}"
+
+        # Activate the service account for Metrics Service.
+        scripts/decrypt_gha_secret.sh scripts/gha-encrypted/metrics_service_access.json.gpg \
+        metrics-access.json "${{ env.METRICS_SERVICE_SECRET }}"
+        gcloud auth activate-service-account --key-file metrics-access.json
     - name: Build and test
       run: |
         FirebaseABTesting=${{ needs.check.outputs.abtesting_run_job }} \
@@ -65,6 +80,11 @@ jobs:
         FirebaseRemoteConfig=${{ needs.check.outputs.remoteconfig_run_job }} \
         FirebaseStorage=${{ needs.check.outputs.storage_run_job }} \
         ./scripts/health_metrics/create_binary_size_report.sh
+      env:
+        PULL_REQUEST_NUM: ${{ github.event.pull_request.number }}
+        BASE_COMMIT: ${{ github.event.pull_request.base.sha }}
+        POSTSUBMIT: ${{ github.event.pull_request.merged }}
+        SOURCE_BRANCH: ${{ github.base_ref }}
 
   pod-lib-lint-abtesting:
     needs: check

+ 27 - 16
scripts/health_metrics/create_binary_size_report.sh

@@ -14,48 +14,59 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# USAGE: git diff -U0 [base_commit] HEAD | get_diff_lines.sh
-#
-# This will generate a JSON output of changed files and their newly added
-# lines.
+# This is to generate pod size report and send to the Metrics Service.
+# The Metrics Service will either
+# 1. save binary size data into the databasae when POSTSUBMIT is true, or
+# 2. post a report in a PR when POSTSUBMIT is not true.
+
+set -ex
 
 BINARY_SIZE_SDK=()
-if $FirebaseABTesting == 'true'; then
+
+# In presubmits, `check` job in the health_metrics.yml workflow will turn on SDK flags if a corresponding
+# file path, in `scripts/health_metrics/code_coverage_file_list.json` is updated.
+# In postsubmits, all SDKs should be measured, so binary size data of all SDKs should be uploaded to a
+# merged commit. Next time a new PR can compare the head of the PR to a commit on the base branch.
+if [[ "${POSTSUBMIT}" == true || "${FirebaseABTesting}" == 'true' ]]; then
   BINARY_SIZE_SDK+=('FirebaseABTesting')
 fi
-if $FirebaseAuth == 'true'; then
+if [[ "${POSTSUBMIT}" == true || "${FirebaseAuth}" == 'true' ]]; then
   BINARY_SIZE_SDK+=('FirebaseAuth')
 fi
-if $FirebaseDatabase == 'true'; then
+if [[ "${POSTSUBMIT}" == true || "${FirebaseDatabase}" == 'true' ]]; then
   BINARY_SIZE_SDK+=('FirebaseDatabase')
 fi
-if $FirebaseDynamicLinks == 'true'; then
+if [[ "${POSTSUBMIT}" == true || "${FirebaseDynamicLinks}" == 'true' ]]; then
   BINARY_SIZE_SDK+=('FirebaseDynamicLinks')
 fi
-if $FirebaseFirestore == 'true'; then
+if [[ "${POSTSUBMIT}" == true || "${FirebaseFirestore}" == 'true' ]]; then
   BINARY_SIZE_SDK+=('FirebaseFirestore')
 fi
-if $FirebaseFunctions == 'true'; then
+if [[ "${POSTSUBMIT}" == true || "${FirebaseFunctions}" == 'true' ]]; then
   BINARY_SIZE_SDK+=('FirebaseFunctions')
 fi
-if $FirebaseInAppMessaging == 'true'; then
+if [[ "${POSTSUBMIT}" == true || "${FirebaseInAppMessaging}" == 'true' ]]; then
   BINARY_SIZE_SDK+=('FirebaseInAppMessaging')
 fi
-if $FirebaseMessaging == 'true'; then
+if [[ "${POSTSUBMIT}" == true || "${FirebaseMessaging}" == 'true' ]]; then
   BINARY_SIZE_SDK+=('FirebaseMessaging')
 fi
-if $FirebasePerformance == 'true'; then
+if [[ "${POSTSUBMIT}" == true || "${FirebasePerformance}" == 'true' ]]; then
   BINARY_SIZE_SDK+=('FirebasePerformance');
 fi
-if $FirebaseRemoteConfig == 'true'; then
+if [[ "${POSTSUBMIT}" == true || "${FirebaseRemoteConfig}" == 'true' ]]; then
   BINARY_SIZE_SDK+=('FirebaseRemoteConfig')
 fi
-if $FirebaseStorage == 'true'; then
+if [[ "${POSTSUBMIT}" == true || "${FirebaseStorage}" == 'true' ]]; then
   BINARY_SIZE_SDK+=('FirebaseStorage')
 fi
 if [ -n "$BINARY_SIZE_SDK" ]; then
   cd scripts/health_metrics/generate_code_coverage_report/
   git clone https://github.com/google/cocoapods-size
   swift build
-  .build/debug/BinarySizeReportGenerator --binary-size-tool-dir cocoapods-size/  --sdk-repo-dir "${GITHUB_WORKSPACE}" --sdk ${BINARY_SIZE_SDK[@]} --log-path "https://github.com/firebase/firebase-ios-sdk/actions/runs/${GITHUB_RUN_ID}"
+  if [[ "${POSTSUBMIT}" == true ]]; then
+    .build/debug/BinarySizeReportGenerator --binary-size-tool-dir cocoapods-size/  --sdk-repo-dir "${GITHUB_WORKSPACE}" --sdk ${BINARY_SIZE_SDK[@]} --merge "firebase/firebase-ios-sdk" --head-commit "${GITHUB_SHA}" --token $(gcloud auth print-identity-token) --log-link "https://github.com/firebase/firebase-ios-sdk/actions/runs/${GITHUB_RUN_ID}" --source-branch "${SOURCE_BRANCH}"
+  else
+    .build/debug/BinarySizeReportGenerator --binary-size-tool-dir cocoapods-size/  --sdk-repo-dir "${GITHUB_WORKSPACE}" --sdk ${BINARY_SIZE_SDK[@]} --presubmit "firebase/firebase-ios-sdk" --head-commit "${GITHUB_SHA}" --token $(gcloud auth print-identity-token) --log-link "https://github.com/firebase/firebase-ios-sdk/actions/runs/${GITHUB_RUN_ID}" --pull-request-num "${PULL_REQUEST_NUM}" --base-commit "${BASE_COMMIT}"
+  fi
 fi

+ 117 - 0
scripts/health_metrics/generate_code_coverage_report/Sources/BinarySizeReportGenerator/BinarySizeReportGeneration.swift

@@ -0,0 +1,117 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import ArgumentParser
+import Foundation
+import Utils
+
+private enum Constants {}
+
+extension Constants {
+  // Binary Size Metrics flag for the Metrics Service.
+  static let metric = "BinarySize"
+  static let cocoapodSizeReportFile = "binary_report.json"
+}
+
+/// Pod Config
+struct PodConfigs: Codable {
+  let pods: [Pod]
+}
+
+struct Pod: Codable {
+  let sdk: String
+  let path: String
+}
+
+/// Cocoapods-size tool report,
+struct SDKBinaryReport: Codable {
+  let combinedPodsExtraSize: Int
+
+  enum CodingKeys: String, CodingKey {
+    case combinedPodsExtraSize = "combined_pods_extra_size"
+  }
+}
+
+/// Metrics Service API request data
+public struct BinaryMetricsReport: Codable {
+  let metric: String
+  let results: [Result]
+  let log: String
+}
+
+public struct Result: Codable {
+  let sdk, type: String
+  let value: Int
+}
+
+extension BinaryMetricsReport {
+  func toData() -> Data {
+    let jsonData = try! JSONEncoder().encode(self)
+    return jsonData
+  }
+}
+
+func CreatePodConfigJSON(of sdks: [String], from sdk_dir: URL) throws {
+  var pods: [Pod] = []
+  for sdk in sdks {
+    let pod: Pod = Pod(sdk: sdk, path: sdk_dir.path)
+    pods.append(pod)
+  }
+  let podConfigs: PodConfigs = PodConfigs(pods: pods)
+  try JSONParser.writeJSON(of: podConfigs, to: "./cocoapods_source_config.json")
+}
+
+// Create a JSON format data prepared to send to the Metrics Service.
+func CreateMetricsRequestData(of sdks: [String], type: String,
+                              log: String) throws -> BinaryMetricsReport {
+  var reports: [Result] = []
+  // Create a report for each individual SDK and collect all of them into reports.
+  for sdk in sdks {
+    // Create a report, generated by cocoapods-size, for each SDK. `.stdout` is used
+    // since `pipe` could cause the tool to hang. That is probably caused by cocopod-size
+    // is using pipe and a pipe is shared by multiple parent/child process and cause
+    // deadlock. `.stdout` is a quick way to resolve at the moment. The difference is
+    // that `.stdout` will print out logs in the console while pipe can assign logs a
+    // variable.
+    Shell.run(
+      "cd cocoapods-size && python3 measure_cocoapod_size.py --cocoapods \(sdk) --cocoapods_source_config ../cocoapods_source_config.json --json \(Constants.cocoapodSizeReportFile)",
+      stdout: .stdout
+    )
+    let SDKBinarySize = try JSONParser.readJSON(
+      of: SDKBinaryReport.self,
+      from: "cocoapods-size/\(Constants.cocoapodSizeReportFile)"
+    )
+    // Append reports for data for API request to the Metrics Service.
+    reports.append(Result(sdk: sdk, type: type, value: SDKBinarySize.combinedPodsExtraSize))
+  }
+  let metricsRequestReport = BinaryMetricsReport(
+    metric: Constants.metric,
+    results: reports,
+    log: log
+  )
+  return metricsRequestReport
+}
+
+public func CreateMetricsRequestData(SDK: [String], SDKRepoDir: URL,
+                                     logPath: String) throws -> BinaryMetricsReport? {
+  try CreatePodConfigJSON(of: SDK, from: SDKRepoDir)
+  let data = try CreateMetricsRequestData(
+    of: SDK,
+    type: "firebase-ios-sdk-testing",
+    log: logPath
+  )
+  return data
+}

+ 55 - 87
scripts/health_metrics/generate_code_coverage_report/Sources/BinarySizeReportGenerator/main.swift

@@ -18,43 +18,9 @@ import ArgumentParser
 import Foundation
 import Utils
 
-private enum Constants {}
-
-extension Constants {
-  // Binary Size Metrics flag for the Metrics Service.
-  static let metric = "BinarySize"
-  static let cocoapodSizeReportFile = "binary_report.json"
-}
-
-/// Pod Config
-struct PodConfigs: Codable {
-  let pods: [Pod]
-}
-
-struct Pod: Codable {
-  let sdk: String
-  let path: String
-}
-
-/// Cocoapods-size tool report,
-struct SDKBinaryReport: Codable {
-  let combinedPodsExtraSize: Int
-
-  enum CodingKeys: String, CodingKey {
-    case combinedPodsExtraSize = "combined_pods_extra_size"
-  }
-}
-
-/// Metrics Service API request data
-struct BinaryMetricsReport: Codable {
-  let metric: String
-  let results: [Result]
-  let log: String
-}
-
-struct Result: Codable {
-  let sdk, type: String
-  let value: Int
+enum RequestType: EnumerableFlag {
+  case presubmit
+  case merge
 }
 
 struct BinarySizeReportGenerator: ParsableCommand {
@@ -70,60 +36,62 @@ struct BinarySizeReportGenerator: ParsableCommand {
   @Option(parsing: .upToNextOption, help: "SDKs to be measured.")
   var SDK: [String]
 
-  @Option(help: "SDKs to be measured.")
-  var logPath: String
+  @Option(help: "Link to the log, leave \"\" if none.")
+  var logLink: String
 
-  func CreatePodConfigJSON(of sdks: [String], from sdk_dir: URL) throws {
-    var pods: [Pod] = []
-    for sdk in sdks {
-      let pod: Pod = Pod(sdk: sdk, path: sdk_dir.path)
-      pods.append(pod)
-    }
-    let podConfigs: PodConfigs = PodConfigs(pods: pods)
-    try JSONParser.writeJSON(of: podConfigs, to: "./cocoapods_source_config.json")
-  }
+  // Send to the Metrics Service
+  @Flag(help: "Determine if the request to Metrics Service is for pull_requests or merge.")
+  var requestType: RequestType
 
-  // Create a JSON format data prepared to send to the Metrics Service.
-  func CreateMetricsRequestData(of sdks: [String], type: String, log: String) throws -> Data {
-    var reports: [Result] = []
-    // Create a report for each individual SDK and collect all of them into reports.
-    for sdk in sdks {
-      // Create a report, generated by cocoapods-size, for each SDK. `.stdout` is used
-      // since `pipe` could cause the tool to hang. That is probably caused by cocopod-size
-      // is using pipe and a pipe is shared by multiple parent/child process and cause
-      // deadlock. `.stdout` is a quick way to resolve at the moment. The difference is
-      // that `.stdout` will print out logs in the console while pipe can assign logs a
-      // variable.
-      Shell.run(
-        "cd cocoapods-size && python3 measure_cocoapod_size.py --cocoapods \(sdk) --cocoapods_source_config ../cocoapods_source_config.json --json \(Constants.cocoapodSizeReportFile)",
-        stdout: .stdout
-      )
-      let SDKBinarySize = try JSONParser.readJSON(
-        of: SDKBinaryReport.self,
-        from: "cocoapods-size/\(Constants.cocoapodSizeReportFile)"
-      )
-      // Append reports for data for API request to the Metrics Service.
-      reports.append(Result(sdk: sdk, type: type, value: SDKBinarySize.combinedPodsExtraSize))
-    }
-    let metricsRequestReport = BinaryMetricsReport(
-      metric: Constants.metric,
-      results: reports,
-      log: log
-    )
-    let data = try JSONEncoder().encode(metricsRequestReport)
-    return data
-  }
+  @Argument(help: "A repo coverage data will be related to.")
+  var repo: String
+
+  @Option(
+    help: "presubmit: compare the diff to the base_commit; merge: store coverage data linking to this commit."
+  )
+  var headCommit: String
+
+  @Option(help: "Token to access an account of the Metrics Service")
+  var token: String
+
+  @Option(
+    help: "This is for presubmit request. Number of a pull request that a coverage report will be posted on."
+  )
+  var pullRequestNum: Int?
+
+  @Option(help: "This is for presubmit request. Additional note for the report.")
+  var pullRequestNote: String?
+
+  @Option(
+    help: "This is for presubmit request. Coverage of commit will be compared to the coverage of this base commit."
+  )
+  var baseCommit: String?
+
+  @Option(
+    help: "This is for merge request. Branch here will be linked to the coverage data, with the merged commit, in the database. "
+  )
+  var sourceBranch: String?
 
   func run() throws {
-    try CreatePodConfigJSON(of: SDK, from: SDKRepoDir)
-    let data = try CreateMetricsRequestData(
-      of: SDK,
-      type: "firebase-ios-sdk-testing",
-      log: logPath
-    )
-    // TODO: Simply print out the report for debug purpose, and will apply the
-    // data to API request sent to the Metrics Service.
-    print(String(decoding: data, as: UTF8.self))
+    if let binarySizeRequest = try CreateMetricsRequestData(
+      SDK: SDK,
+      SDKRepoDir: SDKRepoDir,
+      logPath: logLink
+    ) {
+      sendMetricsServiceRequest(
+        repo: repo,
+        commits: headCommit,
+        jsonContent: binarySizeRequest.toData(),
+        token: token,
+        is_presubmit: requestType == RequestType.presubmit,
+        branch: sourceBranch,
+        pullRequest: pullRequestNum,
+        pullRequestNote: pullRequestNote,
+        baseCommit: baseCommit
+      )
+    } else {
+      print("coverageRequest is nil.")
+    }
   }
 }
 

+ 1 - 0
scripts/health_metrics/generate_code_coverage_report/Sources/CoverageReportGenerator/main.swift

@@ -16,6 +16,7 @@
 
 import ArgumentParser
 import Foundation
+import Utils
 
 enum RequestType: EnumerableFlag {
   case presubmit

+ 4 - 3
scripts/health_metrics/generate_code_coverage_report/Sources/CoverageReportGenerator/MetricsServiceRequest.swift → scripts/health_metrics/generate_code_coverage_report/Sources/Utils/MetricsServiceRequest.swift

@@ -19,9 +19,10 @@ import Foundation
   import FoundationNetworking
 #endif
 
-func sendMetricsServiceRequest(repo: String, commits: String, jsonContent: Data, token: String,
-                               is_presubmit: Bool, branch: String?, pullRequest: Int?,
-                               pullRequestNote: String?, baseCommit: String?) {
+public func sendMetricsServiceRequest(repo: String, commits: String, jsonContent: Data,
+                                      token: String,
+                                      is_presubmit: Bool, branch: String?, pullRequest: Int?,
+                                      pullRequestNote: String?, baseCommit: String?) {
   var request: URLRequest
   var semaphore = DispatchSemaphore(value: 0)
   let endpoint =

+ 1 - 1
scripts/health_metrics/generate_code_coverage_report/Sources/Utils/ShellUtils.swift

@@ -35,7 +35,7 @@ public enum Shell {
     task.arguments = ["-c", command]
     task.launch()
     if displayCommand {
-      print("[CoverageReportParser] Command:\(command)\n")
+      print("[Health Metrics] Command:\(command)\n")
     }
     task.waitUntilExit()
     if stdout == ProcessCom.pipe {