buildscripts: add Kokoro-based CI for Android APK stats (#3984)
diff --git a/buildscripts/kokoro/cronet.cfg b/buildscripts/kokoro/android.cfg
similarity index 65%
rename from buildscripts/kokoro/cronet.cfg
rename to buildscripts/kokoro/android.cfg
index 10302da..7730b90 100644
--- a/buildscripts/kokoro/cronet.cfg
+++ b/buildscripts/kokoro/android.cfg
@@ -1,5 +1,5 @@
# Config file for internal CI
# Location of the continuous shell script in repository.
-build_file: "grpc-java/buildscripts/kokoro/cronet.sh"
+build_file: "grpc-java/buildscripts/kokoro/android.sh"
timeout_mins: 45
diff --git a/buildscripts/kokoro/android.sh b/buildscripts/kokoro/android.sh
new file mode 100755
index 0000000..47ff54c
--- /dev/null
+++ b/buildscripts/kokoro/android.sh
@@ -0,0 +1,95 @@
+#!/bin/bash
+
+set -exu -o pipefail
+cat /VERSION
+
+BASE_DIR="$(pwd)"
+
+# Build Cronet
+
+cd "$BASE_DIR/github/grpc-java/cronet"
+./cronet_deps.sh
+../gradlew --include-build .. build
+
+
+# Install gRPC and codegen for the Android examples
+# (a composite gradle build can't find protoc-gen-grpc-java)
+
+cd "$BASE_DIR/github/grpc-java"
+
+export GRADLE_OPTS=-Xmx512m
+export PROTOBUF_VERSION=3.5.1
+export LDFLAGS=-L/tmp/protobuf/lib
+export CXXFLAGS=-I/tmp/protobuf/include
+export LD_LIBRARY_PATH=/tmp/protobuf/lib
+export OS_NAME=$(uname)
+
+# Proto deps
+buildscripts/make_dependencies.sh
+ln -s "/tmp/protobuf-${PROTOBUF_VERSION}/$(uname -s)-$(uname -p)" /tmp/protobuf
+
+./gradlew install
+
+cd ./examples/android/clientcache
+./gradlew build
+cd ../routeguide
+./gradlew build
+cd ../helloworld
+./gradlew build
+
+
+# Skip APK size and dex count comparisons for non-PR builds
+
+if [[ -z "$KOKORO_GITHUB_PULL_REQUEST_COMMIT" ]]; then
+ echo "Skipping APK size and dex count"
+ exit 0
+fi
+
+
+# Save a copy of set_github_status.py (it may differ from the base commit)
+
+SET_GITHUB_STATUS="$TMPDIR/set_github_status.py"
+cp "$BASE_DIR/github/grpc-java/buildscripts/set_github_status.py" "$SET_GITHUB_STATUS"
+
+
+# Collect APK size and dex count stats for the helloworld example
+
+read -r ignored new_dex_count < \
+ <("${ANDROID_HOME}/tools/bin/apkanalyzer" dex references app/build/outputs/apk/release/app-release-unsigned.apk)
+
+new_apk_size="$(stat --printf=%s app/build/outputs/apk/release/app-release-unsigned.apk)"
+
+
+# Get the APK size and dex count stats using the pull request base commit
+
+cd $BASE_DIR/github/grpc-java
+git checkout HEAD^
+./gradlew install
+cd examples/android/helloworld/
+./gradlew build
+
+read -r ignored old_dex_count < \
+ <("${ANDROID_HOME}/tools/bin/apkanalyzer" dex references app/build/outputs/apk/release/app-release-unsigned.apk)
+
+old_apk_size="$(stat --printf=%s app/build/outputs/apk/release/app-release-unsigned.apk)"
+
+dex_count_delta="$((new_dex_count-old_dex_count))"
+
+apk_size_delta="$((new_apk_size-old_apk_size))"
+
+
+# Update the statuses with the deltas
+
+gsutil cp gs://grpc-testing-secrets/github_credentials/oauth_token.txt ~/
+
+"$SET_GITHUB_STATUS" \
+ --sha1 "$KOKORO_GITHUB_PULL_REQUEST_COMMIT" \
+ --state success \
+ --description "New DEX reference count: $(printf "%'d" "$new_dex_count") (delta: $(printf "%'d" "$dex_count_delta"))" \
+ --context android/dex_diff --oauth_file ~/oauth_token.txt
+
+"$SET_GITHUB_STATUS" \
+ --sha1 "$KOKORO_GITHUB_PULL_REQUEST_COMMIT" \
+ --state success \
+ --description "New APK size in bytes: $(printf "%'d" "$new_apk_size") (delta: $(printf "%'d" "$apk_size_delta"))" \
+ --context android/apk_diff --oauth_file ~/oauth_token.txt
diff --git a/buildscripts/kokoro/cronet.sh b/buildscripts/kokoro/cronet.sh
deleted file mode 100644
index 5097fac..0000000
--- a/buildscripts/kokoro/cronet.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-#!/bin/bash
-
-set -exu -o pipefail
-cat /VERSION
-
-cd ./github/grpc-java/cronet
-./cronet_deps.sh
-../gradlew --include-build .. build
diff --git a/buildscripts/set_github_status.py b/buildscripts/set_github_status.py
new file mode 100755
index 0000000..e8e2593
--- /dev/null
+++ b/buildscripts/set_github_status.py
@@ -0,0 +1,64 @@
+#!/usr/bin/env python2.7
+#
+# Copyright 2018 gRPC authors.
+#
+# 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 argparse
+import json
+import urllib2
+
+
+def run():
+ argp = argparse.ArgumentParser(description='Set status on pull request')
+
+ argp.add_argument(
+ '--sha1', type=str, help='SHA1 of the commit', required=True)
+ argp.add_argument(
+ '--state',
+ type=str,
+ choices=('error', 'failure', 'pending', 'success'),
+ help='State to set',
+ required=True)
+ argp.add_argument(
+ '--description', type=str, help='Status description', required=True)
+ argp.add_argument('--context', type=str, help='Status context', required=True)
+ argp.add_argument(
+ '--oauth_file', type=str, help='File with OAuth token', required=True)
+
+ args = argp.parse_args()
+ sha1 = args.sha1
+ state = args.state
+ description = args.description
+ context = args.context
+ oauth_file = args.oauth_file
+
+ with open(oauth_file, 'r') as oauth_file_reader:
+ oauth_token = oauth_file_reader.read().replace('\n', '')
+
+ req = urllib2.Request(
+ url='https://api.github.com/repos/grpc/grpc-java/statuses/%s' % sha1,
+ data=json.dumps({
+ 'state': state,
+ 'description': description,
+ 'context': context,
+ }),
+ headers={
+ 'Authorization': 'token %s' % oauth_token,
+ 'Content-Type': 'application/json',
+ })
+ print urllib2.urlopen(req).read()
+
+
+if __name__ == '__main__':
+ run()