Update Amplify.swift #18
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Public Interface Breakage Detection | |
on: | |
pull_request: | |
permissions: | |
contents: write | |
pull-requests: write | |
jobs: | |
buildAndCheckAPIBreakage: | |
name: Build and Check API Breakage | |
runs-on: macos-latest | |
steps: | |
- name: Checkout repository | |
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1 | |
with: | |
ref: ${{ github.head_ref }} # Checkout the PR branch | |
fetch-depth: 1 | |
- name: Fetch the branchs | |
run: | | |
git fetch origin ${{ github.sha }} | |
- name: Check if branch is up-to-date with reference branch | |
id: check-up-to-date | |
run: | | |
git fetch origin ${{ github.event.pull_request.base.ref }} | |
echo "${{ github.event.pull_request.base.ref }}" | |
echo "${{ github.event.pull_request.head.ref }}" | |
if git merge-base --is-ancestor ${{ github.event.pull_request.base.ref }} ${{ github.event.pull_request.head.ref }}; then | |
echo "true" | |
echo "is_up_to_date=true" >> $GITHUB_ENV | |
else | |
echo "false" | |
echo "is_up_to_date=false" >> $GITHUB_ENV | |
fi | |
- name: Setup and Run Swift API Diff | |
env: | |
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
run: | | |
# Define the mandatory patterns to filter out | |
mandatory_patterns=( | |
'^/\*' | |
'^$' | |
) | |
# Define the list of exceptions to filter out | |
exceptions=( | |
'has been added as a new enum case$' | |
'is now with @_spi$' | |
) | |
# Function to apply patterns with grep | |
apply_patterns() { | |
local input="$1" | |
local output="$input" | |
# Apply mandatory patterns | |
for pattern in "${mandatory_patterns[@]}"; do | |
output=$(echo "$output" | grep -v "$pattern") | |
done | |
# Apply exceptions | |
for exception in "${exceptions[@]}"; do | |
output=$(echo "$output" | grep -v "$exception") | |
done | |
echo "$output" | |
} | |
echo "Swift version: $(swift --version)" | |
echo "Swift package manager version: $(swift package --version)" | |
swift package resolve | |
# Ensure we are in the correct directory | |
cd $GITHUB_WORKSPACE | |
# Run swift-api-diff commands here directly | |
NEW_API_DIR=$(mktemp -d) | |
OLD_API_DIR=$(mktemp -d) | |
SDK_PATH=$(xcrun --show-sdk-path) | |
# Get all library module names | |
modules=$(swift package dump-package | jq -r '.products | map(select(.name == "Amplify" or .name == "CoreMLPredictionsPlugin" or .name == "AWSDataStorePlugin" or .name == "AWSPluginsCore")) | map(.name) | .[]') | |
echo "Modules: $modules" | |
echo "Fetching old version..." | |
git checkout ${{ github.event.pull_request.base.sha }} | |
built=false | |
for module in $modules; do | |
# If file doesn't exits in the old directory | |
if [ ! -f api-dump/${module}.json ]; then | |
echo "Old API file does not exist in the base branch. Generating it..." | |
# Check if the project has been built | |
if ! $built; then | |
echo "Building project..." | |
swift build > /dev/null 2>&1 || { echo "Failed to build project"; exit 1; } | |
built=true | |
fi | |
# Generate the API file using api-digester | |
swift api-digester -sdk "$SDK_PATH" -dump-sdk -module "$module" -o "$OLD_API_DIR/${module}.json" -I .build/debug || { echo "Failed to dump new SDK for module $module"; exit 1; } | |
else | |
# Use the api-dump/${module}.json file from the base branch directly | |
cp "api-dump/${module}.json" "$OLD_API_DIR/${module}.json" | |
fi | |
done | |
echo "Fetching new version..." | |
git checkout ${{ github.sha }} | |
git log -1 # Print the commit info for debugging | |
swift build> /dev/null 2>&1 || { echo "Failed to build new version"; exit 1; } | |
for module in $modules; do | |
swift api-digester -sdk "$SDK_PATH" -dump-sdk -module "$module" -o "$NEW_API_DIR/${module}.json" -I .build/debug || { echo "Failed to dump new SDK for module $module"; exit 1; } | |
done | |
echo "Comparing APIs" | |
# Compare APIs for each module and capture the output | |
api_diff_output="" | |
for module in $modules; do | |
swift api-digester -sdk "$SDK_PATH" -diagnose-sdk --input-paths "$OLD_API_DIR/${module}.json" --input-paths "$NEW_API_DIR/${module}.json" >> "api-diff-report-${module}.txt" 2>&1 | |
module_diff_output=$(apply_patterns "$(cat "api-diff-report-${module}.txt")") | |
if [ -n "$module_diff_output" ]; then | |
api_diff_output="${api_diff_output}\n**Module: ${module}**\n${module_diff_output}\n" | |
# Check if there are lines containing "has been renamed to Func" | |
if echo "$module_diff_output" | grep -q 'has been renamed to Func'; then | |
# Capture the line containing "has been renamed to Func" | |
renamed_line=$(echo "$module_diff_output" | grep 'has been renamed to Func') | |
# Append a message to the module_diff_output | |
api_diff_output="${api_diff_output}👉🏻 _Note: If you're just adding optional parameters to existing methods, neglect the line:_\n_${renamed_line}_\n" | |
fi | |
fi | |
done | |
# Checkout to the branch associated with the pull request | |
git stash --include-untracked | |
git checkout ${{ github.head_ref }} | |
if [ ! -d "api-dump" ]; then | |
echo "api-dump folder does not exist. Creating it..." | |
mkdir -p "api-dump" | |
fi | |
# Update the api-dump folder of the new version by making a commit if there are changes | |
for module in $modules; do | |
if [ ! -f api-dump/${module}.json ]; then | |
echo "API file does not exist in api-dump folder. Creating it..." | |
echo "{}" > "api-dump/${module}.json" | |
fi | |
if ! diff "$NEW_API_DIR/${module}.json" "api-dump/${module}.json" > /dev/null; then | |
echo "Updating API Dumps..." | |
mv "$NEW_API_DIR/${module}.json" "api-dump/${module}.json" | |
fi | |
done | |
git config --global user.name "aws-amplify-ops" | |
git config --global user.email "aws-amplify@amazon.com" | |
git add api-dump/*.json | |
if ! git diff --cached --quiet --exit-code; then | |
# Get the file names that have changes | |
changed_files=$(git diff --cached --name-only) | |
push_changes=false | |
for file in $changed_files; do | |
if [[ $file == api-dump/* ]]; then | |
# Get the number of lines in the file | |
total_lines=$(wc -l < "$file") | |
# Get the line numbers of the changes | |
changed_lines=$(git diff --cached -U0 "$file" | grep -o '@@ [^ ]* [^ ]* @@' | awk '{print $3}' | cut -d ',' -f1 | sed 's/[^0-9]//g') | |
# Check if any change is not within the last 10 lines | |
for line in $changed_lines; do | |
if [ "$line" -le "$((total_lines - 10))" ]; then | |
push_changes=true | |
break | |
fi | |
done | |
# If any file should be pushed, break out of the loop | |
if [ "$push_changes" = true ]; then | |
break | |
fi | |
fi | |
done | |
if [ "$push_changes" = true ]; then | |
if [ "$is_up_to_date" = "true" ]; then | |
git commit -m "Update API dumps for new version" | |
git push origin HEAD:${{ github.head_ref }} | |
else | |
echo "Branch is not up-to-date with main. Skip API file committing." | |
fi | |
else | |
echo "No changes to commit in the api-dump folder." | |
fi | |
else | |
echo "No changes to commit in the api-dump folder." | |
fi | |
git stash pop || true | |
echo "Generating Comments" | |
echo "API_DIFF_OUTPUT<<EOF" >> $GITHUB_ENV | |
if [ -n "$api_diff_output" ]; then | |
echo "### 💔 Public API Breaking Change detected:" >> $GITHUB_ENV | |
if [ "$is_up_to_date" = "false" ]; then | |
api_diff_output="${api_diff_output}❗️ _Warning: Your branch is not up to date with the reference branch. Please ensure your branch is updated to avoid potential conflicts and ensure accurate API change detection._" | |
fi | |
echo -e "$api_diff_output" >> $GITHUB_ENV | |
echo "EOF" >> $GITHUB_ENV | |
else | |
echo "### ✅ No Public API Breaking Change detected" >> $GITHUB_ENV | |
if [ "$is_up_to_date" = "false" ] && [ "$push_changes" = true ]; then | |
echo "❗️ _Warning: Your branch is not up to date with the reference branch. The API file can not successfully updated._" >> $GITHUB_ENV | |
fi | |
echo "EOF" >> $GITHUB_ENV | |
fi | |
- name: Comment on PR with API Diff | |
uses: actions/github-script@v6 | |
with: | |
github-token: ${{ secrets.GITHUB_TOKEN }} | |
script: | | |
const apiDiffOutput = process.env.API_DIFF_OUTPUT; | |
const isUpToDate = process.env.is_up_to_date === 'true'; | |
const issueNumber = context.payload.pull_request.number; | |
const owner = context.repo.owner; | |
const repo = context.repo.repo; | |
if (apiDiffOutput && apiDiffOutput.trim().length > 0) { | |
github.rest.issues.createComment({ | |
owner: owner, | |
repo: repo, | |
issue_number: issueNumber, | |
body: `## API Breakage Report\n${apiDiffOutput}\n` | |
}); | |
} else { | |
console.log("No API diff output found."); | |
} | |
if (!isUpToDate) { | |
// Fail the job | |
throw new Error("Branch is not up to date with the reference branch."); | |
} |