Skip to content
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

Api change detection improvements #1717

Merged
merged 48 commits into from
Jun 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
752515e
Adding scripts to detect api changes
goergisn Jun 6, 2024
5787fb8
changing github action
goergisn Jun 6, 2024
b3b3765
adjusted script
goergisn Jun 6, 2024
ffebcc2
More changes
goergisn Jun 6, 2024
3cadff7
fixing script
goergisn Jun 6, 2024
d2cf211
small sanitation
goergisn Jun 6, 2024
a8598f1
Remove diffing
goergisn Jun 6, 2024
f4f7754
adding module name
goergisn Jun 7, 2024
dcdc099
Try comment phase
goergisn Jun 7, 2024
3d2cef1
Playing around
goergisn Jun 7, 2024
9a0d19c
Playing around
goergisn Jun 7, 2024
2fe25be
more playing around
goergisn Jun 7, 2024
8944308
Fixing commenting
goergisn Jun 7, 2024
0268bfd
Nicer formatting
goergisn Jun 7, 2024
65f026b
sorting changes by changeDescription
goergisn Jun 7, 2024
b497b0e
removing unused code
goergisn Jun 7, 2024
e187390
Recreating comment to make sure it's one of the last
goergisn Jun 7, 2024
2f2ff62
trying out loops
goergisn Jun 7, 2024
8a321d4
adding complete check
goergisn Jun 7, 2024
9b3659c
formatting
goergisn Jun 10, 2024
43c0de6
Fixed formatting
goergisn Jun 10, 2024
ca20039
fixing git diff
goergisn Jun 10, 2024
09b6eac
trying something different
goergisn Jun 10, 2024
a006d7d
trying something else
goergisn Jun 10, 2024
e2f76d0
maybe this time
goergisn Jun 10, 2024
4b620ba
whatever
goergisn Jun 10, 2024
654e5b9
maybe this works
goergisn Jun 10, 2024
2deb186
Improving comparison
goergisn Jun 10, 2024
6f16208
Updating workflow
goergisn Jun 10, 2024
b1f847c
Removed testing code
goergisn Jun 10, 2024
1606cfa
Trigger action
goergisn Jun 12, 2024
da662b1
fixing
goergisn Jun 12, 2024
308aeea
Removing spi internal additions/removals from log
goergisn Jun 12, 2024
c62c607
extracting find function to Element extension
goergisn Jun 13, 2024
9fd987f
Adding documentation
goergisn Jun 13, 2024
afe29a7
Remove scripts folder from spell check
goergisn Jun 13, 2024
a7929b5
Optimizing speed
goergisn Jun 14, 2024
44b2de7
Merge branch 'develop' into api_change_detection_improvements
goergisn Jun 14, 2024
1f127a9
Merge branch 'develop' into api_change_detection_improvements
goergisn Jun 14, 2024
43177d3
Merge branch 'api_change_detection_improvements' of github.com:Adyen/…
goergisn Jun 14, 2024
2522192
Removed unused code
goergisn Jun 14, 2024
1dc1692
Only running the script on PRs and manually
goergisn Jun 14, 2024
911d6f9
Remove manual run of script
goergisn Jun 14, 2024
c9def42
Merge branch 'develop' into api_change_detection_improvements
goergisn Jun 14, 2024
e7f8c57
Do not add "no changes detected" to output
goergisn Jun 14, 2024
5d09aff
Simplifying generation
goergisn Jun 15, 2024
f226325
Merge branch 'develop' into api_change_detection_improvements
goergisn Jun 19, 2024
a323386
fixing github workflow
goergisn Jun 19, 2024
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
2 changes: 1 addition & 1 deletion .github/workflows/detect_api_changes.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:

- name: 🔍 Detect Changes
run: |
Scripts/generate_public_interface_definition.sh ${branch} ${baseRepo}
Scripts/detect_public_api_changes/compare.sh ${branch} ${baseRepo}
env:
branch: '${{github.event.pull_request.base.ref}}'
baseRepo: '${{github.server_url}}/${{github.repository}}.git'
Expand Down
157 changes: 157 additions & 0 deletions Scripts/detect_public_api_changes/compare.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
#!/bin/bash

# -------------------------------------------------------------------
# Setup & Info
# -------------------------------------------------------------------

BRANCH=$1
REPO=$2
TARGET="x86_64-apple-ios17.4-simulator"
SDK="`xcrun --sdk iphonesimulator --show-sdk-path`"
COMPARISON_VERSION_DIR_NAME="comparison_version_$BRANCH"
DERIVED_DATA_PATH=".build"
SDK_DUMP_INPUT_PATH="$DERIVED_DATA_PATH/Build/Products/Debug-iphonesimulator"
ALL_TARGETS_LIBRARY_NAME="AdyenAllTargets"
MODULE_NAMES=($(./Scripts/detect_public_api_changes/package_file_helper.swift Package.swift -print-available-targets))

# -------------------------------------------------------------------
# Convenience Functions
# -------------------------------------------------------------------

# Echos the entered information for validation
function printRunInfo() {

echo "Branch: " $BRANCH
echo "Repo: " $REPO
echo "Target: " $TARGET
echo "Dir: " $COMPARISON_VERSION_DIR_NAME

echo "Modules:"
for MODULE in ${MODULE_NAMES[@]}; do
echo "-" $MODULE
done
}

# Clones the comparison project into a custom directory
function setupComparisonRepo() {

rm -rf $COMPARISON_VERSION_DIR_NAME
mkdir $COMPARISON_VERSION_DIR_NAME
cd $COMPARISON_VERSION_DIR_NAME
git clone -b $BRANCH $REPO
cd ..
}

# Builds the project and temporarily modifies
# some files to optimize the process
#
# $1 - The path to the current project dir
#
# Examples
#
# buildProject . # If we're in the current project dir
# buildProject ../.. # If we're in the comparison project dir
function buildProject() {

# Removing derived data if available
rm -rf .build

# We have to obscure the project file so `xcodebuild` uses the Package.swift to build the module
mv Adyen.xcodeproj Adyen.xcode_proj

# Copying the Package.swift so we can revert the change done by the next step
cp Package.swift Package.sw_ift

# Modify the Package.swift file to generate a product/library that contains all targets
# so we only have to build it once and can use it to diff all modules
$1/Scripts/detect_public_api_changes/package_file_helper.swift Package.swift -add-consolidated-library $ALL_TARGETS_LIBRARY_NAME

xcodebuild -scheme $ALL_TARGETS_LIBRARY_NAME \
-sdk $SDK \
-derivedDataPath $DERIVED_DATA_PATH \
-destination "platform=iOS,name=Any iOS Device" \
-target $TARGET \
-quiet \
-skipPackagePluginValidation

# Reverting the tmp changes
rm Package.swift
mv Adyen.xcode_proj Adyen.xcodeproj
mv Package.sw_ift Package.swift
}

# Generates an sdk-dump in form of a json file
#
# $1 - The module to generate the dump for
# $2 - The output path for the generated json file
#
# Examples
#
# generateSdkDump "AdyenDropIn" "api_dump.json"
#
function generateSdkDump() {

xcrun swift-api-digester -dump-sdk \
-module $1 \
-o $2 \
-I $SDK_DUMP_INPUT_PATH \
-sdk $SDK \
-target $TARGET
}

# Compares both versions of the provided module
# and writes the result into a .md file (Handled by the diff.swift script)
#
# $1 - The module to compare
#
# Examples
#
# diffModuleVersions "AdyenDropIn"
#
function diffModuleVersions() {

echo "📋 [$1] Generating current sdk dump"
generateSdkDump $1 "api_dump.json"

cd $COMPARISON_VERSION_DIR_NAME/adyen-ios

echo "📋 [$1] Generating comparison sdk dump"
generateSdkDump $1 "../../api_dump_comparison.json"

cd ../..

./Scripts/detect_public_api_changes/diff.swift "api_dump.json" "api_dump_comparison.json" $1

# Cleaning up afterwards
rm api_dump.json
rm api_dump_comparison.json
}

# -------------------------------------------------------------------
# Main Execution
# -------------------------------------------------------------------

printRunInfo

echo "↘️ Setting up comparison project"
setupComparisonRepo

# Move into the comparison project
cd $COMPARISON_VERSION_DIR_NAME/adyen-ios

echo "🛠️ Building '$ALL_TARGETS_LIBRARY_NAME' comparison project"
buildProject ../..

# Move back to the current project dir
cd ../..

echo "🛠️ Building '$ALL_TARGETS_LIBRARY_NAME' current project"
buildProject .

echo "👷 Diffing all Modules"

for MODULE in ${MODULE_NAMES[@]}; do

diffModuleVersions $MODULE

done
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import Foundation

let currentDirectory = URL(fileURLWithPath: FileManager.default.currentDirectoryPath)

// TODO: Pass all modules at once so we can write the file out all at once
// (This also allows us to indicate in the title whether or not there were any changes)
let old = CommandLine.arguments[1]
let new = CommandLine.arguments[2]
let moduleName = CommandLine.arguments[3]
Expand Down Expand Up @@ -62,14 +64,14 @@ class Element: Codable, Equatable, CustomDebugStringConvertible {
if let declKind {
if declKind == "Constructor" {
definition += "func "
} else {
} else {
definition += "\(declKind.lowercased()) "
}
}

definition += "\(printedName)"

if let conformanceNames = conformances?.map({ $0.printedName }), !conformanceNames.isEmpty {
if let conformanceNames = conformances?.map(\.printedName), !conformanceNames.isEmpty {
definition += " : \(conformanceNames.joined(separator: ", "))"
}

Expand Down Expand Up @@ -150,7 +152,7 @@ func recursiveCompare(element lhs: Element, to rhs: Element, oldFirst: Bool) ->
return []
}

if lhs.isSpiInternal && rhs.isSpiInternal {
if lhs.isSpiInternal, rhs.isSpiInternal {
// If both elements are spi internal we can ignore them as they are not in the public interface
return []
}
Expand All @@ -159,12 +161,11 @@ func recursiveCompare(element lhs: Element, to rhs: Element, oldFirst: Bool) ->

// TODO: Add check if accessor changed (e.g. changed from get/set to get only...)

if oldFirst, (
lhs.printedName != rhs.printedName ||
lhs.spiGroupNames != rhs.spiGroupNames ||
lhs.conformances != rhs.conformances ||
lhs.declAttributes != rhs.declAttributes
) {
if oldFirst,
lhs.printedName != rhs.printedName ||
lhs.spiGroupNames != rhs.spiGroupNames ||
lhs.conformances != rhs.conformances ||
lhs.declAttributes != rhs.declAttributes {
// TODO: Show what exactly changed (name, spi, conformance, declAttributes, ...) as a bullet list maybe (add a `changeList` property to `Change`)
changes += [.init(changeType: .change, parentName: lhs.parentPath, changeDescription: "`\(lhs)` ➡️ `\(rhs)`")]
}
Expand Down Expand Up @@ -212,7 +213,7 @@ func compare() throws {
)

if decodedOldDefinition == decodedNewDefinition {
try persistComparison(fileContent: "## 🫧 `\(moduleName)`\n- No changes detected")
try persistComparison(fileContent: "## 🫧 `\(moduleName)` - No changes detected")
return
}

Expand Down
70 changes: 70 additions & 0 deletions Scripts/detect_public_api_changes/package_file_helper.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/usr/bin/env xcrun swift

import Foundation

// MARK: - Convenience Methods

/// Extracts all targets in the targets section
///
/// Returns: All available target names
func availableTargets(from packageContent: String) -> [String] {
let scanner = Scanner(string: packageContent)
_ = scanner.scanUpToString("targets: [")

var availableTargets = Set<String>()

while scanner.scanUpToString(".target(") != nil {
let nameStartTag = "name: \""
let nameEndTag = "\""

_ = scanner.scanUpToString(nameStartTag)
_ = scanner.scanString(nameStartTag)

if let targetName = scanner.scanUpToString(nameEndTag) {
availableTargets.insert(targetName)
}
}

return availableTargets.sorted()
}

/// Generates a library entry from the name and available target names to be inserted into the `Package.swift` file
func consolidatedLibraryEntry(_ name: String, from availableTargets: [String]) -> String {
"""

.library(
name: "\(name)",
targets: [\(availableTargets.map { "\"\($0)\"" }.joined(separator: ", "))]
),
"""
}

/// Generates the updated content for the `Package.swift` adding the consolidated library entry (containing all targets) in the products section
func updatedContent(with consolidatedEntry: String) -> String {
// Update the Package.swift content
var updatedContent = packageContent
if let productsRange = packageContent.range(of: "products: [", options: .caseInsensitive) {
updatedContent.insert(contentsOf: consolidatedEntry, at: productsRange.upperBound)
} else {
print("Products section not found")
}
return updatedContent
}

// MARK: - Main

let packagePath = CommandLine.arguments[1] // Path to the Package.swift file
let packageContent = try String(contentsOfFile: packagePath)
let targets = availableTargets(from: packageContent)

if CommandLine.arguments[2] == "-add-consolidated-library" {
// Inserts a new library into the targets section containing all targets from the target section
let consolidatedLibraryName = CommandLine.arguments[3] // Name of the library containing all targets
let consolidatedEntry = consolidatedLibraryEntry(consolidatedLibraryName, from: targets)
let updatedPackageContent = updatedContent(with: consolidatedEntry)
// Write the updated content back to the file
try updatedPackageContent.write(toFile: packagePath, atomically: true, encoding: .utf8)
} else if CommandLine.arguments[2] == "-print-available-targets" {
// Prints the targets to the console so it can be consumed by the "compare.sh" script and transformed in to an array
print(targets.joined(separator: " "))
}
78 changes: 0 additions & 78 deletions Scripts/generate_public_interface_definition.sh

This file was deleted.

Loading