-
Notifications
You must be signed in to change notification settings - Fork 199
223 lines (187 loc) · 8.59 KB
/
api-breaking-changes-detection.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
name: Public Interface Breakage Detection
on:
pull_request:
permissions:
contents: write
pull-requests: write
jobs:
build-and-check-api-breakage:
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: Setup and Run Swift API Diff
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Define the list of exceptions to filter out
exceptions=(
'has been added as a new enum case$'
'is now with @_spi$'
)
# Define the mandatory patterns to filter out
mandatory_patterns=(
'^/\*'
'^$'
)
# 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
# Moduels with aws-crt-swift as dependency are not listed due to swift-api-digester's issue with analyzing C dependencies
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 fetch origin ${{ github.event.pull_request.base.sha }}
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
# 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
echo "API_DIFF_OUTPUT<<EOF" >> $GITHUB_ENV
if [ -n "$api_diff_output" ]; then
echo "### 💔 Public API Breaking Change detected:" >> $GITHUB_ENV
echo -e "$api_diff_output" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
else
echo "### ✅ No Public API Breaking Change detected" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
fi
# 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 "[email protected]"
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')
echo "Changed lines in $file: $changed_lines"
# 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
git commit -m "Update API dumps for new version"
git push origin HEAD:${{ github.head_ref }}
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
- name: Comment on PR with API Diff
uses: actions/github-script@d7906e4ad0b1822421a7e6a35d5ca353c962f410 # v6.4.1
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const apiDiffOutput = process.env.API_DIFF_OUTPUT;
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.");
}