Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
118 changes: 118 additions & 0 deletions .github/backwards-compatibility-check.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#!/bin/bash
# Copyright 2026 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.

set -e

# USAGE:
#
# backwards-compatibility-check.sh COMPONENT [BASE_REF]
#
# COMPONENT: The component directory name to run the backwards compatibility check for.
# BASE_REF: Optional. The baseline git ref (e.g. 'main', 'origin/main' or a release tag) to compare against. Defaults to 'main'.

if [ "$#" -lt 1 ] || [ "$#" -gt 2 ]; then
echo "usage: backwards-compatibility-check.sh COMPONENT [BASE_REF]"
exit 1
fi

COMPONENT=$1
BASE_REF=${2:-main}

# Exception for 'dev'
if [ "${COMPONENT}" = "dev" ]; then
echo "Skipping dev directory (not a public component)."
exit 0
fi

# Check if component directory exists
if [ ! -d "${COMPONENT}" ]; then
echo "Error: Directory ${COMPONENT} does not exist!" >&2
exit 1
fi

# Check if composer.json exists
COMP_JSON="${COMPONENT}/composer.json"
if [ ! -f "${COMP_JSON}" ]; then
echo "Error: composer.json not found in ${COMPONENT}!" >&2
exit 1
fi

echo "Checking backwards compatibility for component: ${COMPONENT} against baseline: ${BASE_REF}" >&2

# Check if the component existed in the baseline reference
if ! git rev-parse --verify "${BASE_REF}:${COMPONENT}" >/dev/null 2>&1; then
echo "Component ${COMPONENT} did not exist in baseline ${BASE_REF}. Skipping check (all additions)." >&2
exit 0
fi

# Create a temporary directory
TMP_DIR=$(mktemp -d)

# Initialize a dummy git repo inside TMP_DIR so roave-backward-compatibility-check can compare revisions
(
cd "${TMP_DIR}"
git init -q
)

# Extract baseline files from the BASE_REF, stripping the prefix folder so they land at the root of TMP_DIR
if ! git archive "${BASE_REF}" "${COMPONENT}" | tar -x --strip-components=1 -C "${TMP_DIR}" 2>/dev/null; then
echo "Error: Failed to archive and extract files for ${COMPONENT} from git ref ${BASE_REF}." >&2
rm -rf "${TMP_DIR}"
exit 1
fi

(
cd "${TMP_DIR}"
git add -A
git commit -q -m "Base state from ${BASE_REF}"
)

# Copy the current local component files over the baseline repository,
# making sure to exclude vendor directories or composer-local files.
echo "Applying local changes from ${COMPONENT} to the baseline clone..." >&2
rsync -a --exclude="vendor/" --exclude="composer-local.json" "${COMPONENT}/" "${TMP_DIR}/"

# Commit the changes in the cloned split repository so we can compare them
CODE=0
if (
cd "${TMP_DIR}"
git add -A

# Check if there are any changes to commit
if ! git diff --cached --quiet; then
git commit -q -m "Apply local PR changes"
echo "Running Roave Backward Compatibility Check..." >&2

# Locate the roave binary portably
COMPOSER_BIN=$(composer global config bin-dir --absolute 2>/dev/null || echo ~/.composer/vendor/bin)
ROAVE_BIN=$(command -v roave-backward-compatibility-check || echo "${COMPOSER_BIN}/roave-backward-compatibility-check")

if ! "${ROAVE_BIN}" --from=HEAD~1 --format=github-actions; then
echo "❌ BC Breaks detected in ${COMPONENT}!" >&2
exit 1
else
echo "✅ No BC Breaks detected in ${COMPONENT}." >&2
fi
else
echo "No files modified for ${COMPONENT} compared to ${BASE_REF}. Skipping check." >&2
fi
); then
CODE=0
else
CODE=1
fi

rm -rf "${TMP_DIR}"
exit $CODE
60 changes: 27 additions & 33 deletions .github/workflows/release-checks.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,49 +20,43 @@ jobs:
uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # v2
with:
php-version: "8.1"
- name: "Rebase PR branch onto main branch"
if: github.event.pull_request.user.login != 'release-please[bot]'
env:
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
git config user.name "Github Actions"
git config user.email "actions@github.com"
git checkout "$HEAD_SHA"
if ! git rebase origin/main; then
echo "Failed to rebase PR branch onto main. There may be conflicts. Please resolve conflicts or rebase manually."
exit 1
fi
- name: "Install dependencies"
run: composer global require "roave/backward-compatibility-check:^8.2"
- name: "Check for BC breaks"
if: github.event.pull_request.user.login != 'release-please[bot]'
run: |
~/.composer/vendor/bin/roave-backward-compatibility-check --from=origin/main --format=github-actions
- name: "Check for BC label"
env:
HAS_BC_LABEL: ${{ contains(github.event.pull_request.title, '!:') }}
run: |
if [[ "true" == "${HAS_BC_LABEL}" ]]; then
echo "Breaking change label found in PR title"
exit 1
fi
- name: Get Latest Release
if: github.event.pull_request.user.login == 'release-please[bot]'
id: latest-release
uses: pozetroninc/github-action-get-latest-release@5ac6001ed175b443484c81ab32a20e12132be99c # master
with:
repository: ${{ github.repository }}
token: ${{ secrets.GITHUB_TOKEN }}
- name: "Check for BC breaks (Next Release)"
if: github.event.pull_request.user.login == 'release-please[bot]'
# We've already approved and justified the breaking changes. Run the check but continue on error
continue-on-error: true
- name: "Check for BC breaks"
if: github.event.pull_request.user.login != 'release-please[bot]'
run: |
~/.composer/vendor/bin/roave-backward-compatibility-check \
--from=${STEPS_LATEST_RELEASE_OUTPUTS_RELEASE} \
--to=origin/main --format=github-actions
env:
STEPS_LATEST_RELEASE_OUTPUTS_RELEASE: ${{ steps.latest-release.outputs.release }}
git config --global user.name "Github Actions"
git config --global user.email "actions@github.com"

BASE_REF="origin/main"

# Find changed component directories
CHANGED_COMPONENTS=$(git diff ${BASE_REF} --name-only | grep -E '^[A-Z][a-zA-Z0-9]*/' | cut -d'/' -f1 | sort -u || true)
if [ -z "${CHANGED_COMPONENTS}" ]; then
echo "No public component directories have changed."
exit 0
fi

FAIL=""
for COMPONENT in ${CHANGED_COMPONENTS}; do
if ! bash .github/backwards-compatibility-check.sh "${COMPONENT}" "${BASE_REF}"; then
FAIL="true"
fi
done

if [ "${FAIL}" = "true" ]; then
exit 1
elif [ "${IS_RELEASE_PR}" != "true" ] && [[ "true" == "${{ contains(github.event.pull_request.title, '!:') }}" ]]; then
echo "❌ Error: You indicated breaking changes in your PR title (!:), but no backwards compatibility breaks were detected by the automated check."
exit 1
fi

# Ensure the release PR does not contain an unexpected (e.g. 2.0.0) major version release
# Add "MAJOR_VERSION_ALLOWED=component1,component2" to the PR description to allow major version
Expand Down
1 change: 1 addition & 0 deletions Gax/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ composer.lock
composer-test.lock
vendor/
build/artifacts/
tests/Conformance/showcase.pem
artifacts/
.idea
.metadata
Expand Down
Loading