Skip to content

ci: Test upper bound dependenices file in Github CI #3877

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions .github/scripts/test_dependency_compatibility.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#!/bin/bash

# This script generates a maven command to test unit and integration tests for
# the repo. The outputted maven command will be in the rough following format
# `mvn verify ... -D{dependency.name}.version={dependency.version]`. The variables
# ${dependency.name} and ${dependency.version} are parsed from the input to the script.
#
# Default invocation ./.github/scripts/test_dependency_compatibility.sh will use the default
# upper-bounds dependency file at the root of the repo.
# There are two potential inputs to the script:
# 1. -f {file}: Custom file/path for the upper-bound dependencies to test
# 2. -l {deps_list}: Comma-separated list of dependencies to test (e.g. protobuf=4.31.0,guava=33.4.8-jre)
# Note: Do not include the `-D` prefix or `.version` suffix. Those values will be appended when generating
# the maven command.
#
# If both inputs are supplied, the deps_list input has precedence. For Github Actions workflow,
# the default workflow will run with the upper-bounds file. A `workflow_dispatch` option takes in
# an input for the deps_list to manually run a subset of dependencies.
#
# The default upper-bound dependencies file is `dependencies.txt` located in the root
# of sdk-platform-java. The upper-bound dependencies file will be in the format of:
# ${dependency.name}=${dependency.version}

set -ex

function print_help() {
echo "Unexpected input argument for this script."
echo "Use -f {file} for the directory of the upper-bound dependencies file."
echo "Use -l {deps_list} for a comma-separated list of dependencies to test (Format: dep1=1.0,dep2=2.0)"
}

# Function to parse a dependency string and append it to the Maven command
function add_dependency_to_maven_command() {
local dep_pair=$1
if [[ ! "${dep_pair}" =~ .*=.* ]]; then
echo "Malformed dependency string: ${dep_pair}. Expected format: dependency=version"
exit 1
fi
local dependency=$(echo "${dep_pair}" | cut -d'=' -f1 | tr -d '[:space:]')
local version=$(echo "${dep_pair}" | cut -d'=' -f2 | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
MAVEN_COMMAND+=" -D${dependency}.version=${version}"
}

# Default to the upper bounds file in the root of the repo
file='dependencies.txt'
dependency_list=''

# The colon (:) after the letter means that there is an input associated with the flag
while getopts 'f:l:' flag; do
case "${flag}" in
f) file="${OPTARG}" ;;
l) dependency_list="${OPTARG}" ;;
*) print_help && exit 1
esac
done

# Error if both the file and deps_list inputs is empty
if [[ -z "${file}" && -z "${dependency_list}" ]]; then
print_help && exit 1
fi

MAVEN_COMMAND="mvn verify -Penable-integration-tests -Dclirr.skip -Dcheckstyle.skip -Dfmt.skip -Denforcer.skip "

# Check if a list of dependencies was provided as an argument. If the list of dependency inputted
# is empty, then run with the upper-bound dependencies file
if [ -z "${dependency_list}" ]; then
UPPER_BOUND_DEPENDENCY_FILE=$file

if [ ! -e "${UPPER_BOUND_DEPENDENCY_FILE}" ]; then
echo "The inputted upper-bound dependency file '${UPPER_BOUND_DEPENDENCY_FILE}' cannot be found"
exit 1
fi

# Read the file line by line
while IFS= read -r line; do
# Ignore any comments and blank lines
if [[ "${line}" =~ ^[[:space:]]*# ]] || [[ -z "${line}" ]]; then
continue
fi
add_dependency_to_maven_command "${line}"
done < "${UPPER_BOUND_DEPENDENCY_FILE}"
else # This else block means that a list of dependencies was inputted
# Set the Internal Field Separator (IFS) to a comma.
# This tells 'read' to split the string by commas into an array named DEPS.
# The 'read -ra' command reads the input into an array.
IFS=',' read -ra DEPS <<< "${dependency_list}"

# Loop through each item in the DEPS array.
for DEP_PAIR in "${DEPS[@]}"; do
# Skip any empty items that might result from trailing commas.
if [ -z "${DEP_PAIR}" ]; then
continue
fi
add_dependency_to_maven_command "${DEP_PAIR}"
done
fi

# Run the generated maven command to test with the dependency versions
$MAVEN_COMMAND
72 changes: 72 additions & 0 deletions .github/workflows/dependency_compatibility_test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
name: Dependency Compatibility Test

on:
pull_request:
branches:
- 'main'
workflow_dispatch:
inputs:
dependencies-list:
description: 'Comma-separated list of dependencies to test (Example format: protobuf=4.31.0,guava=33.4.8-jre).
Do not include the `-D` prefix or `.version` suffix. Those values will be appended when generating
the command. No input (default) will run the the upper-bound dependencies file.'
required: false
default: ''

jobs:
dependency-compatibility-test:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Checkout sdk-platform-java
uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
cache: maven
# The workflow_dispatch event is for team members who want to manually test certain dependencies + version combos
# The normal workflow is not from `workflow_dispatch` and will use the default upper-bounds dependencies file
- name: Determine Inputted Dependencies List
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.dependencies-list != '' }}
run: echo "DEPENDENCIES_LIST=${{ github.event.inputs.dependencies-list }}" >> $GITHUB_ENV
# Install the modules for the rest of the CI
- name: Install sdk-platform-java's modules
# gapic-generator-java requires Java 8 and is irrelevant for this CI
run: mvn -q -B -ntp install --projects '!gapic-generator-java' -Dcheckstyle.skip -Dfmt.skip -DskipTests -Dclirr.skip -T 1C

# Run in the root module which should test for everything except for showcase
- name: Perform Dependency Compatibility Testing
shell: bash
run: |
if [[ -n "${{ env.DEPENDENCIES_LIST }}" ]]; then
./.github/scripts/test_dependency_compatibility.sh -l ${{ env.DEPENDENCIES_LIST }}
else
./.github/scripts/test_dependency_compatibility.sh
fi
# Set up local showcase server to run the showcase ITs
- name: Parse showcase version
working-directory: java-showcase/gapic-showcase
run: echo "SHOWCASE_VERSION=$(mvn help:evaluate -Dexpression=gapic-showcase.version -q -DforceStdout)" >> "$GITHUB_ENV"
- name: Install showcase server
run: |
sudo mkdir -p /usr/src/showcase
sudo chown -R ${USER} /usr/src/
curl --location https://github.com/googleapis/gapic-showcase/releases/download/v${{env.SHOWCASE_VERSION}}/gapic-showcase-${{env.SHOWCASE_VERSION}}-linux-amd64.tar.gz --output /usr/src/showcase/showcase-${{env.SHOWCASE_VERSION}}-linux-amd64.tar.gz
cd /usr/src/showcase/
tar -xf showcase-*
./gapic-showcase run &
cd -
# Run specifically for showcase
- name: Perform Dependency Compatibility Testing (Showcase only)
shell: bash
# Need to cd out of the directory to get the scripts as this step is run inside the java-showcase directory
run: |
if [[ -n "${{ env.DEPENDENCIES_LIST }}" ]]; then
../.github/scripts/test_dependency_compatibility.sh -l ${{ env.DEPENDENCIES_LIST }}
else
../.github/scripts/test_dependency_compatibility.sh -f ../dependencies.txt
fi
working-directory: java-showcase
44 changes: 44 additions & 0 deletions dependencies.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# This file contains a list of dependencies and their versions to be tested for compatibility.
# The format is key=value, where the key is the dependency name and the value is the version.
# "1P" refers to First-Party dependencies (owned by Google).
# "3P" refers to Third-Party dependencies.

# 1P Parent-Pom
javax.annotation-api=1.3.2
grpc=1.74.0
google.auth=1.37.1
google.http-client=1.47.1
gson=2.13.1
guava=33.4.8-jre
protobuf=4.31.1
opentelemetry=1.52.0
errorprone=4.15.1
j2objc-annotations=3.0.0
threetenbp=1.7.1
slf4j=2.0.17

# 1P Shared-Deps
grpc-gcp=1.6.1
google.oauth-client=1.39.0
google.api-client=2.8.0

# 3P Shared-Deps
threeten-extra=1.8.0
opencensus=0.31.0
findbugs=3.0.2
jackson=2.19.2
errorprone=2.41.0
codec=1.19.0
httpcomponents.httpcore=4.4.16
httpcomponents.httpclient=4.5.14
apache-httpclient-5=5.5
apache-httpcore-5=5.3.4
perfmark-api=0.27.0
j2objc-annotations=3.0.0
google.cloud.opentelemetry=0.36.0
opentelemetry-semconv=1.34.0
flogger=0.9
arrow=18.3.0
dev.cel=0.10.1
com.google.crypto.tink=1.18.0
io.opentelemetry.contrib.opentelemetry-gcp-resources=1.48.0-alpha
Loading